溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Google在構(gòu)建靜態(tài)代碼分析工具方面的實例分析

發(fā)布時間:2022-01-17 17:16:42 來源:億速云 閱讀:149 作者:柒染 欄目:網(wǎng)絡(luò)安全

這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)Google在構(gòu)建靜態(tài)代碼分析工具方面的實例分析,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

軟件bug耗費開發(fā)者和軟件公司大量的時間和金錢。 以2014年為例,被廣泛使用的SSL協(xié)議實現(xiàn)中的一個(“goto fail”)bug導(dǎo)致可接受無效的SSL證書,另外一個與日期格式化相關(guān)的bug導(dǎo)致Twitter的大范圍服務(wù)中斷。此類錯誤通常可以被靜態(tài)分析檢測,其實事實上在閱讀代碼或文檔階段均可以快速識別,可以最終現(xiàn)實是情況仍然在生產(chǎn)環(huán)境實施發(fā)生。

之前的工作已經(jīng)完善報道將bug檢測工具應(yīng)用于軟件開發(fā)的經(jīng)驗。但雖然開發(fā)人員使用靜態(tài)分析工具方面有如此多的成功案例,仍有以下原因?qū)е鹿こ處煵⒉豢偳樵甘褂渺o態(tài)分析工具或主動忽略工具產(chǎn)生的告警信息:

  • 未合理整合。工具未集成到開發(fā)人員的工作流程中或者是程序運行時間太長;

  • 無效的告警。告警信息可行性性差;

  • 不值得信賴。用戶因為誤報而不再信任結(jié)果;

  • 缺陷實際利用場景不清晰。報告的bug在理論上是可行的,但缺陷在實際利用場景下并不清晰;

  • 修復(fù)成本過高。修復(fù)已檢測到的代碼缺陷的成本太高或有其他方面的風(fēng)險;

  • 告警不易理解。使用者并不了解告警信息的的具體信息和原理。

接下來本文描述了我們?nèi)绾挝oogle在先前使用FindBug進行java語言分析方面以及學(xué)術(shù)文獻中的得到的經(jīng)驗和教訓(xùn),最終在Google公司成功構(gòu)建了軟件工程師日常使用的靜態(tài)分析基礎(chǔ)設(shè)施架構(gòu)。借助吸收于工程師的意見建議,Google的工具可以在有問題的代碼被合入到公司級別的代碼倉庫之前檢測到每天工程師所修復(fù)的數(shù)千個問題。

在工具作用范圍方面,我們專注于將靜態(tài)分析融入為Google核心開發(fā)流程的一部分,并服務(wù)于大部分Google開發(fā)人員。許多靜態(tài)代碼分析工具在部署于google的20億行代碼級別下將相形見絀,因此大規(guī)模場景下運行復(fù)雜分析的技術(shù)的優(yōu)先級并不高。

當(dāng)然必須要考量到Google外部開發(fā)人員在專業(yè)領(lǐng)域(例如航空航天和醫(yī)學(xué)設(shè)備領(lǐng)域)工作的可能會使用特定的靜態(tài)分析工具和工作流程。同樣開發(fā)項目涉及特定類型(例如內(nèi)核代碼和設(shè)備驅(qū)動程序)的開發(fā)人員可能需要進行特定的分析方法。靜態(tài)分析方面已經(jīng)有很多卓越的工作成果,我們并不認(rèn)為我們所反饋的經(jīng)驗和心得是獨一無二的,但我們堅信整理和分享我們在提高google的代碼質(zhì)量和改善開發(fā)體驗方面的工作是有所裨益的。

術(shù)語定義。我們使用以下術(shù)語定義:分析工具對源代碼運行一個或多種“檢查器”,并識別出可能呈現(xiàn)出軟件故障的“缺陷”。如果開發(fā)人員在看到問題后沒有采取積極行動,如果開發(fā)人員遇到識別出的缺陷并未采取適當(dāng)?shù)男迯?fù)方式,我們將其視為“實際誤報”。如果靜態(tài)分析并未準(zhǔn)確的識別報告到某項缺陷,但開發(fā)人員主動采取措施修改此處代碼以提高可讀性和可維護性,那么這就不算是有效的“誤報”。如果一個分析報告了實際的代碼錯誤,但是開發(fā)人員因未理解這處代碼問題因而沒有采取任何行動,就視為這是一個“實際誤報”。我們使用這種概念區(qū)別強調(diào)了研發(fā)角度的重要性。開發(fā)人員而不是工具作者對感知并直接影響到工具的誤報率。

谷歌如何編譯構(gòu)建軟件

下文我們將概述Google軟件開發(fā)流程的關(guān)鍵點。在Google,幾乎所有開發(fā)工具(開發(fā)環(huán)境除外)都是集中化和標(biāo)準(zhǔn)化的。該基礎(chǔ)架構(gòu)的許多部分都是由內(nèi)部團隊擁有的Scratch構(gòu)建的,保留了具備實驗性質(zhì)的靈活性。

源代碼控制和代碼所有權(quán)。Google已開發(fā)并使用單源的代碼管理系統(tǒng)。并試驗單分支存儲(幾乎)所有的Google專有代碼。開發(fā)人員使用限制分支的“基于trunk”的開發(fā)方式,通常以版本發(fā)布而不是特性進行劃分。任何工程師經(jīng)代碼所有者的批準(zhǔn)都可以更改任何代碼。代碼所有權(quán)基于路徑劃分;目錄的所有者對子目錄擁有同樣權(quán)限。

系統(tǒng)構(gòu)建。 Google代碼庫中的所有代碼都經(jīng)一個編譯環(huán)節(jié)獨立的Bazel版本進行編譯,也就是說,所有輸入都必須在源代碼控制中顯式聲明和存儲以便構(gòu)建環(huán)節(jié)的易于分發(fā)性和并行化處理。在Google的構(gòu)建系統(tǒng)中Java規(guī)則依賴于JDK和經(jīng)源碼管理的Java編譯器,并且可以通過快捷引入新版本來為所有使用者更新這些二進制文件。構(gòu)建通常來自源代碼(通過head),幾乎很少有二進制組件簽入分支。因為所有開發(fā)人員都使用相同的構(gòu)建系統(tǒng),所以任何代碼都可以編譯成功而不報錯。

分析工具。 Google使用的靜態(tài)分析工具通常并不復(fù)雜。Google基礎(chǔ)架構(gòu)不支持在這樣級別的系統(tǒng)上運行過程間或基于程序的完整性分析,也沒有大規(guī)模使用高級靜態(tài)分析技術(shù)(例如separation logic技術(shù))。即便是簡單的檢查器也需要分析基礎(chǔ)架構(gòu)來支持對工作流程進行集成。已部署作為一般開發(fā)流程的分析器類型包括:

  • 樣式檢查器(例如Checkstyle,Pylint,和Golint);

  • 可擴展bug查找編譯器(例如Error Prone,ClangTidy, Clang Thread SafetyAnalysis, Govet,和CheckerFramework),包括但不限于抽象語法樹模式匹配工具,基于類型的檢查器和檢測未調(diào)用變量的分析器。

  • 調(diào)用生產(chǎn)服務(wù)的分析器(例如檢查代碼注釋中提到的員工是否仍然在Google上工作) );

  • 檢查構(gòu)建輸出的屬性(例如產(chǎn)出二進制文件的大?。?/p>

Google的C++ linter可以捕獲“goto fail”漏洞,其檢查if語句是否后面有括號。基于模式匹配的檢查器會識別出日期格式化錯誤,那么導(dǎo)致Twitter宕機的代碼就不會在Google編譯通過。谷歌開發(fā)人員也使用動態(tài)分析工具(如AddressSanitizer)來尋找緩沖區(qū)漏洞、使用ThreadSanitizerto查找數(shù)據(jù)競爭問題。這些工具在測試環(huán)節(jié),有時甚至是有生產(chǎn)流量的環(huán)境中運行。

集成開發(fā)環(huán)境(IDE)。靜態(tài)分析問題在開發(fā)過程早期切入點是集成在IDE中。但是Google開發(fā)人員使用各種各樣的編輯器,因此在調(diào)用構(gòu)建工具之前很難一致地檢測所有開發(fā)人員的錯誤。雖然谷歌確實使用與流行的內(nèi)部IDE集成的分析,但要求具有可分析的特定IDE前途漫漫,路險而艱。

測試。幾乎所有Google代碼都包含相應(yīng)的測試環(huán)節(jié),從單元測試到大規(guī)模集成測試。測試活動是系統(tǒng)構(gòu)建中首先需融合的理念,就如編譯環(huán)節(jié)一樣都是獨立和分布式的。對于大多數(shù)項目,開發(fā)人員編寫并維護代碼的測試用例;項目通常沒有單獨的測試或QA組。

Google的持續(xù)構(gòu)建和測試系統(tǒng)會在每次代碼提交時運行測試,會及時反饋由于開發(fā)人員的代碼更改導(dǎo)致構(gòu)建失敗或測試用例不通過。它還支持在提交之前測試變更處以避免破壞項目依賴關(guān)系。

Code review。每次提交到Google的代碼都會首先通過code review。雖然任何開發(fā)人員都可以對Google任何代碼處進行更改,但代碼的所有者必須在提交合入之前review才批準(zhǔn)更改。此外,即使是代碼所有者也必須在提交變更之前review其代碼。代碼審查通過一個與其他開發(fā)基礎(chǔ)設(shè)施緊密集成的集中式、基于Web的工具進行。靜態(tài)分析結(jié)果可在代碼審查中展現(xiàn)。

代碼發(fā)布。谷歌團隊頻繁發(fā)版,大部分的發(fā)布驗證和部署過程都是通過“push on green”的方法自動完成的,這意味著難以依靠進行辛勞的手動發(fā)布驗證過程。如果Google工程師在生產(chǎn)環(huán)境中發(fā)現(xiàn)錯誤,同必須中斷服務(wù)相比而言可以以相對較低的成本回退新版本并將其部署到生產(chǎn)服務(wù)器。

從FindBugs學(xué)習(xí)到的

從2008年到2010年期間早期摸索研究階段,谷歌的靜態(tài)分析技術(shù)專注于使用FindBug進行Java分析:由馬里蘭大學(xué)的William Pugh和賓夕法尼亞州約克學(xué)院的DavidHovemeyer創(chuàng)建的獨立工具。其原理為分析已編譯的Java類文件并提取可以導(dǎo)致bug的代碼結(jié)構(gòu)模型。截至2018年1月,F(xiàn)indBugs在google僅作為極少數(shù)工程師使用的命令行工具。一個名為“BugBot”的小型Google團隊與原作者Pugh合作,有三次大的嘗試試圖將FindBugs集成到Google開發(fā)流程中。

我們通過嘗試學(xué)到了以下幾點:

嘗試1:Bug dashboard。最初在2006年,F(xiàn)indBugs被整合為一個集中性的工具來每晚掃描整個谷歌代碼庫,將生成的結(jié)果入庫方便工程師通過dashboard進行檢視。盡管FindBugs在谷歌的Java代碼庫中發(fā)現(xiàn)了數(shù)百個錯誤,但是該dashboard效果微乎其微,因為錯誤信息dashboard脫離于日常開發(fā)流程,而且同現(xiàn)有的其他靜態(tài)分析結(jié)果不能有機結(jié)合。

嘗試2:集中改進bug。

接下來BugBot團隊開始手動分類每晚查找到的新問題,識別處理相對重要的bug報告。 2009年5月,數(shù)百名Google工程師參加了全公司范圍的“Fix it”周活動,聚焦解決FindBugs的告警問題.總共審查了3954個告警信息(占總數(shù)9473的42%),但實際上只修復(fù)了16%(640個)。實際44%的報告結(jié)果(1746個)已經(jīng)提交了bug反饋跟蹤。 盡管Fixit活動證實FindBugs發(fā)現(xiàn)的許多問題都是現(xiàn)實存在的代碼缺陷,但很大一部分并沒有重要到需要實際采取修復(fù)措施的地步。人工手動分類問題、提交bug報告在大規(guī)模環(huán)境下是難以持續(xù)進行的。

嘗試3:集成于代碼審查。接下來BugBot團隊集成實現(xiàn)了這樣的系統(tǒng):當(dāng)review人員得到待review通知時,F(xiàn)indBugs將會自動運行并將掃描結(jié)果作為代碼審查的注釋展示出來,以上代碼review團隊已經(jīng)針對編碼規(guī)范/風(fēng)格問題已經(jīng)實現(xiàn)完成。谷歌開發(fā)人員可以忽略誤報,并針對FindBugs的結(jié)果可信度進行篩選。該工具進一步嘗試僅顯示新的FindBugs告警,但有時會因為錯誤的分類將其視為新問題。隨著代碼審查工具在2011年被替換,這種集成隨著壽終正寢,原因有兩個:實際工作中的高誤報率導(dǎo)致開發(fā)人員對工具失去信心,開發(fā)人員自由定制進行過濾導(dǎo)致各方對分析結(jié)果觀點不一致。

納入編譯流程

在FindBugs的實驗同時期,Google的C++開發(fā)流程通過向Clang編譯器添加新的檢查規(guī)則而得到不斷進步。Clang團隊實現(xiàn)了的新的編譯器檢查器,包括修復(fù)建議信息,使用ClangMR以分布式方法在整個Google代碼庫上運行經(jīng)過更新的編譯器來優(yōu)化檢查,并編碼實現(xiàn)修復(fù)了代碼庫中的存量bug問題。一旦代碼庫已標(biāo)記被修復(fù)存量問題,Clang團隊就會應(yīng)用新檢查器將新問題標(biāo)記為編譯器錯誤(而不是告警,Clang團隊發(fā)現(xiàn)谷歌開發(fā)人員會忽略告警)來中止構(gòu)建,對此必須加以解決才能通過。Clang團隊通過這一策略非常成功地改進了代碼庫質(zhì)量。

我們遵循這個思路并在javac編譯器基礎(chǔ)之上構(gòu)建了一個簡單易用的基于模式分析的Java靜態(tài)分析工具,名為Error Prone。推出的第一個檢查規(guī)則名為PreconditionsCheckNotNull,用于檢測程序運行之初是否方法檢測入?yún)榭?,比如checkNotNull(“uid為null”,uid)而不是checkNotNull(uid,“uid was null”)。

為了在不破壞任何連續(xù)構(gòu)建的情況下啟動PreconditionsCheckNotNull這樣的檢查器,Error Prone團隊使用它對基于javac的MapReduce程序?qū)φ麄€代碼庫運行此類檢查,類比ClangMR,使用FlumeJava構(gòu)建稱之為JavacFlume。JavacFlume會生成一系列的修復(fù)建議,比對其中的不同,然后應(yīng)用這些到整個代碼庫進行修復(fù)。Error Prone團隊使用內(nèi)部工具Rosie,將大規(guī)模代碼變更拆分為小的變更處,每個改變只會影響到單個項目并測試這些變更處,并將它們發(fā)送給相應(yīng)的團隊進行代碼審查。團隊僅審查適用于其代碼的修復(fù)方案,并且僅在批準(zhǔn)通過它們進行合入,Rosie才會提交實際更改。最終所以存量問題的修復(fù)和變更都得到通過,已有缺陷均得到處理。團隊正式開啟了編譯器錯誤的方式。

當(dāng)我們對收到這些補丁的開發(fā)人員進行調(diào)查反饋時,其中57%收到合入代碼的fix方案的人的樂于得到此類信息,,41%的人持中立態(tài)度。只有2%的人反應(yīng)較為消極會說:“這樣僅僅會加重我的工作量”

編譯器檢查的價值

編譯錯誤是在開發(fā)流程的早期顯示并已集成到開發(fā)人員工作流程中。我們發(fā)現(xiàn)擴展編譯檢查器有效地提高了Google的代碼質(zhì)量。因為Error Prone中的檢查是內(nèi)部針對javac的抽象語法樹而不是字節(jié)碼(與FindBugs不同)編寫的,所以團隊外部的開發(fā)者可以相對容易地進行檢查。利用這些外部貢獻對于提高Error Prone的整體影響力至關(guān)重要。截至2018年1月,共有162名作者提供了733項檢查器。

越早報告問題越好

Google的集中構(gòu)建系統(tǒng)會記錄所有構(gòu)建過程和構(gòu)建結(jié)果,因此我們確保所有的用戶在指定時間窗口可看到其中的錯誤消息。我們向最近遇到編譯器錯誤的開發(fā)人員和已收到針對同一問題修復(fù)建議進行修復(fù)的開發(fā)人員發(fā)送了一項調(diào)查反饋。谷歌開發(fā)者認(rèn)為在編譯時標(biāo)記出來的問題(與合入代碼時期的補丁相對)會捕獲更重要的錯誤;例如,調(diào)查參與者認(rèn)為74%的問題在編譯時被標(biāo)記為“切實問題”,而在合入代碼中發(fā)現(xiàn)的問題只有21%。此外,調(diào)查參與者認(rèn)為在編譯時發(fā)現(xiàn)的問題中有6%(在合入代碼階段中為0%)是“重要級別”。這個結(jié)果可以通過“幸存者偏差效應(yīng)”來解釋; 也就是說在代碼提交時錯誤很可能是由更高昂的手段(如測試和代碼審查)捕獲的。將盡可能多的檢查前置到編譯器中是一種避免這些成本花銷的可靠辦法。

編譯器檢查的標(biāo)準(zhǔn)

為了規(guī)模推廣我們的工作,因為中斷編譯將是一個較大的動作,所以我們已經(jīng)定義了在編譯器中啟用檢查的標(biāo)準(zhǔn),設(shè)置為嚴(yán)格高標(biāo)注模式。Google上的編譯器檢查應(yīng)當(dāng)簡單易讀、可操作的且易于修復(fù)(盡量實現(xiàn)的錯誤消息提示應(yīng)該包括可通用實現(xiàn)的修復(fù)建議); 沒有產(chǎn)生有效誤報(分析動作不應(yīng)中斷構(gòu)建實際上正確的無誤代碼); 并僅報告反饋真實的bug而非風(fēng)格或編碼規(guī)范方面的問題。 衡量滿足這些標(biāo)準(zhǔn)的分析器的主要目標(biāo)不僅僅是簡單檢測問題,而是在整個代碼庫中自動修復(fù)這些編譯器錯誤。 但是這些標(biāo)準(zhǔn)也限制了Error Prone團隊在編譯代碼時啟用的檢查范圍; 許多問題不能被準(zhǔn)確檢測或通用的問題修復(fù)環(huán)節(jié)仍然是擺在我們面前的問題。

在代碼review階段展示告警信息

一旦Error Prone團隊構(gòu)建實現(xiàn)了在編譯時檢測問題所需的基礎(chǔ)設(shè)施架構(gòu),業(yè)已證明該方法切實有效,我們希望查找出更多有高影響因子的bug,bug不局限于我們做的編譯器錯誤檢查和為Java和C++以外的語言提供分析結(jié)果。靜態(tài)分析結(jié)果的第二個集成切入點是Google的代碼審查工具-Critique;靜態(tài)分析結(jié)果通過使用Tricorder在Google的程序分析平臺的Critique中顯示。截至2018年1月,Google的C ++和Java版本的編譯器錯誤均清零,所有分析結(jié)果都顯示在編譯器錯誤或在代碼審查階段。

代碼審查檢查的標(biāo)準(zhǔn)

與編譯時檢查不同,代碼審查期間顯示的分析結(jié)果允許囊括達到10%的有效誤報率。在代碼審查期間所期望的反饋并不總是完美無缺的,并且開發(fā)者在實際采用之前需評估相應(yīng)的修復(fù)建議。 Google在代碼審計階段的檢查器應(yīng)符合以下幾個標(biāo)準(zhǔn):

易于理解。對于工程師來說是明白無誤易于理解的;

方案可行,易于修復(fù)。修復(fù)程序可能需要比編譯器檢查階段花費更多的時間、思考和工夫,檢查結(jié)果應(yīng)包括有關(guān)如何界定問題的指導(dǎo)內(nèi)容;

不到10%的有效誤報率。開發(fā)人員應(yīng)該感受到檢查器在至少90%的情況下均找到實際bug缺陷;

對代碼質(zhì)量產(chǎn)生重大影響。發(fā)現(xiàn)的問題可能不會影響程序正確運行,但開發(fā)人員應(yīng)該認(rèn)真對待它們并選擇修復(fù)它們。

有些問題嚴(yán)重到足以在編譯器中標(biāo)記出來,致力于但降低或開發(fā)自動修復(fù)方案并非可行。例如有的修復(fù)問題可能需要對代碼進行重構(gòu)。將這些檢查結(jié)果作為編譯器錯誤啟用將需要手動清理現(xiàn)有的實現(xiàn),這在Google這樣大規(guī)模的代碼庫上是不可行的。分析工具在代碼審查中顯示這些檢查可避免引入新問題,允許開發(fā)人員決定是否采取措施進行恰當(dāng)?shù)男迯?fù)。代碼審查也是報告相對不太重要的問題(如規(guī)范問題或簡化優(yōu)化代碼)的良好時機。根據(jù)我們的經(jīng)驗,在編譯階段出報告對開發(fā)人員總是難以欣然接受的,并且使得快速迭代和調(diào)試變得更加困難;舉例來說,一處針對代碼路徑不可達的檢測器可能會阻礙一處用于調(diào)試的代碼塊。但在代碼審查時,開發(fā)人員正在細(xì)心準(zhǔn)備完成他們的代碼;他們正處在虛心接受的心態(tài),更容易接受可讀性和風(fēng)格細(xì)節(jié)的問題。

Tricorder

Tricorder設(shè)計理念旨在易于擴展,并支持包括靜態(tài)和動態(tài)分析工具的眾多不同類型的程序分析工具。我們在Tricorder中展示了一些無法作為編譯器錯誤啟用的Error Prone檢查器。Error Prone還創(chuàng)造了一套新的C++分析組件,它與Tricorder集成稱之為ClangTidy。Tricorder分析器的報告支持超過30種語言的結(jié)果,支持簡單的語法分析如樣式檢查器,利用Java,JavaScript和C ++的編譯器信息,并且可以直接與生產(chǎn)數(shù)據(jù)集成(例如關(guān)于當(dāng)前正在運行的任務(wù)作業(yè))。Tricorder持續(xù)在Google取得成功是因為它是支持分析器編寫者的一個生態(tài)平臺的插件模型,并在代碼審查過程中高亮顯示可行的修復(fù)方案,并提供反饋渠道以改進分析器并確保分析器開發(fā)人員對正向反饋采取措施。

使用戶能夠做出貢獻。截至2018年1月,Tricorder包括146個分析器,其中125個來自Tricorder團隊之外,7個插件系統(tǒng)用于數(shù)百個額外檢查(例如ErrorProne和ClangTidy,它們是包括在七個分析插件系統(tǒng)中的兩個)。

審閱者參與進來提供修復(fù)建議。

Tricorder檢查器可以提供為代碼審查工具提供代碼review人員和開發(fā)者可見的合理修復(fù)建議。審閱者可以通過單擊分析結(jié)果上的“請修復(fù)”按鈕來要求開發(fā)者修復(fù)缺陷代碼。直到他們的所有評論(手動和自動發(fā)現(xiàn)的)都得到解決之前,review者通常都不會批準(zhǔn)合入代碼變更。

迭代來自用戶的反饋。除了“請修復(fù)”按鈕,Tricorder還提供了一個“無用”按鈕,評論者或提議者可以點擊表示他們不認(rèn)同分析發(fā)現(xiàn)的結(jié)果。點擊動作會自動附帶提交bug跟蹤器中的錯誤,并將其指向分析器所屬團隊。 Tricorder團隊跟進這些“無用”的點擊標(biāo)記,計算“請修復(fù)”與“無用”之間的點擊比率。如果分析器的比例超過10%那么Tricorder團隊會禁用該分析器直到作者對其進行改進。雖然Tricorder團隊很少有永久性得禁用分析器,它已經(jīng)禁用了一些分析器(在幾個場景下),直到分析器作者刪除和修改完成那些結(jié)果繁雜無用的檢查器。

提交的錯誤通常能改進分析器效果,從而大大提高開發(fā)人員對這些分析器的滿意度;例如,Error Prone團隊在2014年開發(fā)了一個檢查項,它會標(biāo)記出來當(dāng)Guava中傳遞太多參數(shù)傳遞給類似printf這樣的的函數(shù)。類似printf的函數(shù)實際上并不接受所有printf說明符,只接受%S。大約每周一次Error Prone團隊將收到一個“無用”bug,聲稱該分析不正確,實際上bug匹配代碼中的格式統(tǒng)配符數(shù)量與實際傳遞的參數(shù)數(shù)量相匹配。而用戶試圖傳遞%s以外的通配占位符時,任何情況下分析器其實都是正確誤區(qū)的。因此團隊將代碼檢視說明文本更改為直接聲明該函數(shù)僅接受%s占位符并停止獲取有關(guān)該檢查的錯誤。

Tricorder的使用規(guī)模。截至2018年1月,Tricorder已經(jīng)分析了每天大約50000次代碼審查變更。在高峰時段每秒進行三次分析。review者每天點擊“請修復(fù)”超過5000次,作者每天應(yīng)用自動修復(fù)方案約3000次。Tricorder分析器每天收到250次“無用”點擊反饋。

代碼審查分析的成功表明它在Google的開發(fā)人員工作流程中占據(jù)了“最佳位置”。在編譯時顯示的分析結(jié)果必須達到相對到的的質(zhì)量和準(zhǔn)確度,而依靠分析器不可能滿足來繼續(xù)識別嚴(yán)重問題。在review和代碼合入之后,開發(fā)人員進行更改所面臨的阻力會有所增加。因此,開發(fā)人員對已經(jīng)測試和發(fā)布的代碼進行修改時會比較糾結(jié)并且不太可能去解決低危和不太重要的問題。很多其他軟件開發(fā)組織中的分析項目(例如針對Android / iOS應(yīng)用程序的Facebook Infer分析)也強調(diào)代碼審查是報告分析結(jié)果的關(guān)鍵切入點。

擴展分析器

隨著Google開發(fā)者對Tricorder分析器的結(jié)果取得認(rèn)可,他們繼續(xù)要求深入擴展分析器。 Tricorder以兩種方式解決這個問題:允許在項目級定制化并在開發(fā)流程的其他環(huán)節(jié)添加展示分析結(jié)果。在本節(jié)中,我們還討論Google尚未利用更復(fù)雜的分析技術(shù)作為其核心開發(fā)流程的部分原因。

項目級定制

并非所有請求的分析器對于整個Google代碼庫中都具有同等價值;例如一些分析器有較高的誤報率有關(guān),因此具有相應(yīng)的高誤報率的檢查器可能需要在特定的項目啟用配置才有效。這些分析器僅對適合的團隊才有用。

為了實現(xiàn)這些需求,我們的目標(biāo)是使Tricorder達成可定制化。我們之前為FindBugs定制的經(jīng)驗實踐效果較差;基于用戶級別的定制化導(dǎo)致團隊內(nèi)部和團隊之間出現(xiàn)差異化導(dǎo)致工具使用率下降。因為每個用戶都可以看到不同的問題視圖,所以沒有辦法確保每個從事同樣項目工作的人都能看到特定的問題。如果開發(fā)人員從他們團隊的代碼中刪除了所有未使用的導(dǎo)包,那么即使其他一個開發(fā)人員在刪除未使用的導(dǎo)包方面不一致,該變更會很快被回退拒絕。

為了避免此類問題,Tricorder僅允許在項目級別進行配置,確保對特定項目進行更改的任何人都能看到與該項目相關(guān)的分析結(jié)果一致視圖。維護結(jié)果視圖的一致性使得幾種類型的分析器能夠執(zhí)行以下動作:

產(chǎn)生二分結(jié)果。例如,Tricorder包括用于協(xié)議緩沖區(qū)定義的分析器,其識別不向后兼容的變化。開發(fā)人員團隊使用它來確保序列化形式的協(xié)議緩沖區(qū)中的持久信息,但對于不以此形式存儲數(shù)據(jù)的團隊而言則很煩人。另一個例子是有分析器建議使用對于不能使用這些庫或語言功能的項目,對Guava或Java代碼實現(xiàn)沒有意義;

需要特定的設(shè)置或代碼內(nèi)注釋。例如,如果他們的代碼被適當(dāng)?shù)刈⑨?,團隊僅可使用Checker Framework的null ness去分析。另一項分析器是在合理配置后,將檢查特定Android二進制文件的二進制大小和函數(shù)調(diào)用次數(shù)的增長,并警告開發(fā)人員是否是預(yù)期增長或者是否接近限制范圍;

支持特定領(lǐng)域的語言(DSL)和特定于團隊的編碼指南。一些Google軟件開發(fā)團隊開發(fā)了一些小型DSL并且希望運行的相關(guān)檢查器。其他團隊已經(jīng)實現(xiàn)在可讀性和可維護性方面的最佳實踐并希望繼續(xù)執(zhí)行這些檢查;

同時是資源高度利用化的。按照包含動態(tài)分析的結(jié)果混合分析的案例。這樣的分析為一些團隊提供部分高價值,但對所有人來說成本太高或耗時太多。

截至2018年1月,Google內(nèi)部大約有70個可選分析,其中2500個項目至少啟用了其中一個。整個公司的數(shù)十個團隊正在積極開發(fā)新的分析器,大多數(shù)隸屬于都在開發(fā)工具組之外。

其他工作流程集成點

隨著開發(fā)人員對這些工具的信任度增高,他們還要求進一步集成到工作流程中。Tricorder現(xiàn)在通過提供命令行工具,持續(xù)集成系統(tǒng)和代碼審閱工具提供分析結(jié)果。

命令行支持。Tricorder團隊為開發(fā)人員添加了命令行支持,這些開發(fā)人員實際上是代碼管理員,經(jīng)常瀏覽并清理團隊代碼庫中的各種告警分析。這些開發(fā)人員也非常熟悉每個分析器將生成的修復(fù)類型,并且高度信任特定分析器。因此開發(fā)人員可以使用命令行工具自動應(yīng)用給定分析中的所有修復(fù)并進行清理變更;

代碼提交門檻。有些團隊希望特定的分析器可以阻止代碼提交而不是僅僅出現(xiàn)在代碼審查工具中。通常要求阻止提交的能力是由具有高度定制檢查器且保證沒有誤報的團隊提出,通常用在自定義DSL或庫。

代碼展示結(jié)果。代碼展示最適合顯示大型項目(或整個代碼庫)中問題規(guī)模。例如,瀏覽有關(guān)已棄用API的代碼時的分析結(jié)果可以顯示遷移工作需要多少工作量;或者某些安全和隱私分析是全球性的,需要專業(yè)團隊在確定是否存在問題之前審查結(jié)果。由于默認(rèn)情況下不顯示分析結(jié)果,因此代碼瀏覽器允許特定團隊啟用分析視圖,然后掃描整個代碼庫并審核結(jié)果,而這并不會干擾其他開發(fā)人員對這些分析器的注意力。如果分析結(jié)果具有關(guān)聯(lián)修復(fù),則開發(fā)人員只需單擊代碼瀏覽工具即可應(yīng)用此修復(fù)。代碼瀏覽器也非常適合顯示生產(chǎn)數(shù)據(jù)利用的分析結(jié)果,因為在代碼提交和運行之前這些數(shù)據(jù)均不可用。

復(fù)雜分析

在Google上廣泛部署的所有靜態(tài)分析都相對簡單,盡管有些團隊針對特定領(lǐng)域(例如Android應(yīng)用程序)的項目特定分析框架進行過程間分析。 Google規(guī)模的過程分析在技術(shù)上是可行的。但是實施起來這樣的分析非常具有挑戰(zhàn)性。上面說到所有Google的代碼都存貯在單獨的整體的源碼倉庫中,因此從概念上講代碼倉庫中的任何代碼都可以是任意二進制文件的一部分。因此可以想象這樣一種情況,其中特定代碼審查的分析結(jié)果將需要分析整個代碼倉庫。盡管Facebook的Infer專注于過程間分析,將基于分離邏輯的分析器擴展到數(shù)百萬行的代碼庫,但將這種分析器擴展到Google的數(shù)十億行代碼倉庫仍然需要大量的工程化工作。截至2018年1月,實施一個更復(fù)雜的分析系統(tǒng)并不是Google的優(yōu)先考慮因素:

大量投資。前期基礎(chǔ)設(shè)施投資將是令人望而卻步的;

需要努力降低誤報率。分析團隊必須開發(fā)技術(shù),以顯著降低許多分析器的誤報率和/或嚴(yán)格限制該顯示出來哪些錯誤信息,就如圖infer做的一樣;

還有更多要實施。分析團隊仍然有更多“簡易”的分析器需要去實現(xiàn)和集成;

高昂的前期成本。我們發(fā)現(xiàn)這種“簡單”分析器的性價比很高,這是FindBugs的核心動機。相比之下,即使確定更復(fù)雜的檢查器的成本ROI,前期成本也很高。

請注意,對于在專業(yè)領(lǐng)域(例如航空航天和醫(yī)療設(shè)備)或特定項目(例如設(shè)備驅(qū)動程序和手機應(yīng)用程序)上工作的Google以外的開發(fā)人員,此ROI可能會有很大差異。

心得

我們嘗試將靜態(tài)分析融入到Google工作流程中的學(xué)費教會以下我們寶貴的經(jīng)驗教訓(xùn):

發(fā)現(xiàn)bug缺陷很容易。當(dāng)代碼庫足夠龐大時,它幾乎包含任何可以想象得到的代碼模式。即使在具有完整測試覆蓋率和嚴(yán)格的代碼審查流程的成熟代碼庫中錯誤也在若隱若現(xiàn)。有時候問題在本地檢查中并不明顯,有時候由看似人畜無害的重構(gòu)所引入錯誤。例如考慮以下代碼片段使用類型為long的字段f,

result =
31 * result

  • (int) (f ^ (f >>> 32));

想象下如果開發(fā)人員將f的類型更改為int會發(fā)生什么。代碼繼續(xù)編譯,但是向右偏移32變?yōu)閚o-op操作,字段與自身進行異或,變量的hash值變?yōu)槌A?.結(jié)果是f不再影響hashCode方法生成的值。任何能夠計算f類型的工具都可以正確地檢測到向右偏移超過31的情況,我們在Google的代碼庫中修復(fù)了了31項出現(xiàn)該錯誤的代碼,同時在Error Pone中將該檢查納入編譯器錯誤。

由于發(fā)現(xiàn)錯誤很容易,Google使用簡單的工具來檢測錯誤類型。接下來分析編寫者根據(jù)運行Google代碼的結(jié)果進行微調(diào)。

大多數(shù)開發(fā)人員都不會如他們所想的那樣使用靜態(tài)分析工具。隨著許多商業(yè)工具的發(fā)展,Google最初依賴FindBugs的實施,工程師選擇訪問集中的dashboard來查看他們項目中所發(fā)現(xiàn)的問題,但是其中很少有人真正去這樣查看。查找已合入代碼中的錯誤(可能已部署并在沒有用戶可感知到問題的情況下運行)為時已晚。為了確保大多數(shù)或所有工程師都能看到靜態(tài)分析警告,必須將分析工具集成到工作流程中,并默認(rèn)為每個人啟用。Error Prone等項目不提供錯誤dashboard,而是通過額外的檢查器擴展編譯器,并且在代碼審查時展示分析結(jié)果。

開發(fā)者的感受至關(guān)重要。根據(jù)我們的經(jīng)驗和材料積累,許多嘗試將靜態(tài)分析集成到軟件開發(fā)組織的嘗試都失敗了。在Google管理層通常沒有授權(quán)工程師使用靜態(tài)分析工具。從事靜態(tài)分析的工程師必須通過有效實際數(shù)據(jù)證明其影響力。要使靜態(tài)分析項目取得成功開發(fā)者必須感知到他們從中受益并享受使用它的價值。

為了構(gòu)建成功的分析平臺,我們構(gòu)建了可為開發(fā)人員提供高價值的工具。 Tricorder團隊會仔細(xì)核閱已修復(fù)的問題,實際調(diào)研以了解開發(fā)人員的感受,使得通過分析工具提交bug更為便捷,并使用所有這些數(shù)據(jù)來持續(xù)改進。開發(fā)人員需要建立對分析工具的信任。如果一個工具浪費了開發(fā)人員時間的誤報和反饋低級別問題,那么開發(fā)人員就會失去信心并忽視結(jié)果。

不局限于發(fā)現(xiàn)錯誤,修復(fù)它們。要推廣靜態(tài)分析工具,一種典型的方法是列舉代碼庫中存在的大量問題。目的是通過指出糾正潛在錯誤或去在未來阻止bug發(fā)生來影響采取措施。但是如果開發(fā)人員受到不激勵他們采取行動,那么這種潛在預(yù)期結(jié)果仍將無法實現(xiàn)。這是一個基本缺陷:分析工具通過它們識別的問題數(shù)來衡量它們的實用性,而流程集成會由于只有極少數(shù)的bug修復(fù)而失敗。相反Google靜態(tài)分析團隊會同找bug一樣也負(fù)責(zé)相應(yīng)地修復(fù)工作,將其作為是否成功閉環(huán)的標(biāo)準(zhǔn)。專注于修復(fù)錯誤確保了工具提供可行的建議并最大限度地減少誤報。在許多情況下,修復(fù)錯誤就像通過自動化工具找到它們一樣容易。即使對于難以解決的問題,過去五年的研究也凸顯了自動創(chuàng)建靜態(tài)分析問題修復(fù)方面的新技術(shù)。

分析器開發(fā)需群策齊力。雖然特定的靜態(tài)分析工具需要專家開發(fā)人員編寫分析,但專家可能很少實際上并不知道哪些檢查會產(chǎn)生較大的影響因子。此外分析器專家通常不是特定領(lǐng)域?qū)<遥ɡ缒切┦褂肁PI,語言和安全方面的專家)。通過FindBugs集成只有少數(shù)Google員工了解如何編寫新檢查器,因此小型BugBot團隊必須自己完成所有工作。這限制了添加新檢查的速度并事實上不能由其他人從他們的領(lǐng)域知識貢獻而獲益。像Tricorder這樣的團隊現(xiàn)在專注于降低開發(fā)人員提供的檢查標(biāo)準(zhǔn),不需要事先具備靜態(tài)分析經(jīng)驗。例如Google工具Refaster允許開發(fā)人員通過在代碼片段之前和之后指定示例來編寫檢查器。由于貢獻者在自己調(diào)試錯誤代碼之后經(jīng)常有動力做出貢獻,因此新的檢查會逐步節(jié)省開發(fā)人員時間。

結(jié)論

我們的體會心得是重視集成于開發(fā)流程是靜態(tài)分析工具實施的關(guān)鍵。雖然檢查器工具作者可能認(rèn)為開發(fā)人員應(yīng)該面對他們編寫的代碼中存在缺陷列表感到高興,但實際上我們并未發(fā)現(xiàn)這樣的列表會激勵開發(fā)人員去修復(fù)這些缺陷。作為分析工具開發(fā)人員,我們必須通過實際糾正的缺陷方面來定義衡量效果,而不是給開發(fā)人員提供數(shù)字。這意味著我們的責(zé)任遠(yuǎn)遠(yuǎn)超出了分析工具本身。

我們倡導(dǎo)一個專注于盡早推動工作流程集成的系統(tǒng)。盡可能將檢查器作為編譯器錯誤啟用。為了避免中斷構(gòu)建工具編寫者首先承擔(dān)修復(fù)代碼庫中所有現(xiàn)有問題的任務(wù),允許我們不斷前行一步一步地提高Google代碼庫的質(zhì)量。由于我們在編譯器中呈現(xiàn)出錯誤告警,開發(fā)人員在編寫代碼后立即對其進行處理,這樣他們?nèi)匀豢梢赃M行及時更改。為實現(xiàn)這一目標(biāo),我們開發(fā)了基礎(chǔ)架構(gòu)用于運行分析并在整個龐大的Google代碼庫中生成修復(fù)程序。我們還受益于代碼審查和允許更改數(shù)百個文件的提交自動化,當(dāng)然還有工程文化,其通常容許合入遺留代碼的變更,因為改進代碼勝過對修改風(fēng)險的厭惡。

代碼審查是提交代碼之前顯示分析警告的最佳切入點。為了確保開發(fā)人員能夠接受分析結(jié)果Tricorder僅在開發(fā)人員在提交更改之前的修改代碼階段才會展示問題,并且Tricorder團隊?wèi)?yīng)用一系列標(biāo)準(zhǔn)來選擇要顯示的告警。Tricorder進一步在代碼審查工具中收集統(tǒng)計數(shù)據(jù),該工具用于檢測分析器產(chǎn)生大量無效告警的根因。

為了克服告警被忽視,我們努力重新贏得得谷歌工程師的信任,發(fā)現(xiàn)谷歌開發(fā)人員有強烈的偏見去忽視靜態(tài)分析,任何誤報率不理想的報告都給他們不作為的理由。分析團隊非常謹(jǐn)慎只有在根據(jù)描述客觀標(biāo)準(zhǔn)對其進行審查后才能將檢查結(jié)果作為錯誤或警告顯示,因此開發(fā)人員很少被分析結(jié)果淹沒,混淆或煩惱。調(diào)查和反饋渠道是這一過程的重要質(zhì)量控制方法?,F(xiàn)在開發(fā)人員已經(jīng)對分析結(jié)果重新抱有信任感,Tricorder團隊正在滿足在Google開發(fā)人員工作流程中更多介入更多分析的需求。

我們在Google上構(gòu)建了一個成功的靜態(tài)分析基礎(chǔ)架構(gòu),在編譯時和代碼審查期間可防止每天有數(shù)百個錯誤進入Google代碼庫。我們希望其他人可以從我們的經(jīng)驗中獲益,將靜態(tài)分析成功整合到他們自己的工作流程。

上述就是小編為大家分享的Google在構(gòu)建靜態(tài)代碼分析工具方面的實例分析了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI