由Facebook開(kāi)源的靜態(tài)分析工具Infer,現(xiàn)已支持使用RacerD檢測(cè)Java代碼中競(jìng)爭(zhēng)條件。RacerD使用鎖機(jī)制或@ThreadSafe注解,識(shí)別類(lèi)中各方法間的競(jìng)爭(zhēng)條件。
去年,F(xiàn)acebook就已在生產(chǎn)代碼中使用了RacerD,并在代碼提交生產(chǎn)前檢測(cè)到了一千多處的多線程問(wèn)題?,F(xiàn)在,如果Java開(kāi)發(fā)人員使用Infer去檢測(cè)Java代碼中的軟件缺陷,同樣可以使用RacerD的并發(fā)檢測(cè)能力。
競(jìng)爭(zhēng)條件是一種并發(fā)錯(cuò)誤或軟件缺陷。如果兩個(gè)訪問(wèn)同一對(duì)象的線程(其中至少有一個(gè)需要做寫(xiě)操作)間沒(méi)有做適當(dāng)?shù)耐讲僮?,這時(shí)就會(huì)引發(fā)競(jìng)爭(zhēng)條件,進(jìn)而導(dǎo)致線程的執(zhí)行存在彼此重疊。并發(fā)問(wèn)題難以調(diào)試,更難以在發(fā)生問(wèn)題后重現(xiàn)現(xiàn)場(chǎng)。
RacerD可以大規(guī)??焖俨l(fā)地執(zhí)行一些有用的分析。RacerD之所以可以做快速分析,原因在于它在檢測(cè)并發(fā)問(wèn)題時(shí)并沒(méi)有非力圖去檢查整個(gè)代碼庫(kù),而是僅檢查那些它認(rèn)為是并發(fā)運(yùn)行的代碼。
RacerD檢查的類(lèi)、方法和接口定義中可并發(fā)運(yùn)行的代碼。這些代碼或者是使用@ThreadSafe注解的、或者是根據(jù)關(guān)鍵字synchronized所創(chuàng)建鎖而識(shí)別的。如果一個(gè)類(lèi)或結(jié)構(gòu)使用了@ThreadSafe注解,那么RacerD也會(huì)評(píng)估該類(lèi)或?qū)崿F(xiàn)的所有子類(lèi)。為增加代碼覆蓋,RacerD還額外添加了一些有用的注解,包括@ThreadConfined、@Functional、@ReturnsOwnership和@VisibleForTesting。
啟動(dòng)RacerD分析,需要在命令行調(diào)用命令infer。該命令可與其它Infer分析一并運(yùn)行,也可以與只允許RacerD運(yùn)行的infer --racerd-only命令一并運(yùn)行。例如,輸入命令infer --racerd-only -- javac StockPortfolio.java,將會(huì)對(duì)StockPortfolio.java運(yùn)行RacerD。
下面給出一個(gè)例子代碼。RacerD在檢查該例子代碼時(shí),會(huì)對(duì)其中的競(jìng)爭(zhēng)條件給出警告。
@ThreadSafepublic class StockPortfolio { int shares = 0; public void buy(int count) { if (count > 0) { shares += count; } } public int sell(int count){ if (count >= 0 && shares - count >= 0) { shares -= count; return shares; } else { return 0; } }}RacerD會(huì)發(fā)現(xiàn)上面代碼中的軟件缺陷:
Read/Write race. Public method int StockPortfolio.sell(int) reads from field StockPortfolio.shares. Potentially races with writes in methods void StockPortfolio.buy(int), int StockPortfolio.sell(int)可以看到,RacerD對(duì)代碼中包含有未保護(hù)寫(xiě)、讀寫(xiě)競(jìng)爭(zhēng)等給出了警告。當(dāng)前RacerD具有局限性,它只檢測(cè)數(shù)據(jù)競(jìng)爭(zhēng)情況,并不檢測(cè)其它一些并發(fā)問(wèn)題,例如死鎖或原子性。在下面一些情況下,RacerD會(huì)漏掉其中的數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題:
別名(aliasing); 本地定義對(duì)象溢出了范圍; 使用不同的鎖訪問(wèn)受保護(hù)對(duì)象; 本地對(duì)象包含有非屬主對(duì)象; 使用了弱引用內(nèi)存,以及Java的volatile關(guān)鍵字。RacerD的這些局限性,源自于其設(shè)計(jì)目標(biāo)針對(duì)的是降低誤報(bào)率,即便會(huì)導(dǎo)致一些漏報(bào)。
RacerD的共同作者Sam Blackshear和Peter O'Hearn在一份聲明中指出:
Infer當(dāng)前已在Facebook使用,一種方式是批處理部署,另一種方式是作為參與代碼審核的機(jī)器人。部署用于代碼審核的Infer,是作為Facebook持續(xù)集成系統(tǒng)的一部分運(yùn)行。對(duì)于開(kāi)發(fā)人員提交的每次代碼更改,持續(xù)集成將Infer與其它一些編譯和測(cè)試任務(wù)一并運(yùn)行。
RacerD的代碼開(kāi)源提供在GitHub上。更多細(xì)節(jié),可參見(jiàn)用戶指南。
查看英文原文: Facebook Open-Sources RacerD - Java Race Condition Detector