前言 實際工作中,我們經(jīng)常面臨版本迭代節(jié)奏快、需求多、測試時間短、代碼量大等現(xiàn)象,當我們決定深入理解代碼實現(xiàn)的時候,經(jīng)常會遇到以下兩個問題:如何進行代碼分析?優(yōu)先分析哪些代碼? 代碼分析的關(guān)鍵詞:5步法+風險控制+類關(guān)系 5步法操作如下: 1、選取分析對象 2、明確模塊名 3、理清模塊間的關(guān)系 4、確定模塊間的接口 5、從接口著手去梳理代碼結(jié)構(gòu)關(guān)系 測試周期短、代碼量大,從哪里著手開始分析、或者優(yōu)先分析哪些呢? 代碼分析順序的關(guān)鍵詞:風險控制。所謂風險控制,從問題嚴重度和模塊特點兩個方面來說: 1、問題嚴重度:模塊出現(xiàn)問題后,被用戶感知的嚴重程度。按照問題嚴重度順序做代碼分析。比如,驅(qū)動層代碼出問題會“藍屏”,應用層出問題會“crash”、'無反應',腳本出問題,腳本掛掉,用戶側(cè)基本無感知。即藍屏>crash>無反應>無感知,所以最優(yōu)先分析的是驅(qū)動層代碼,其次是應用層代碼,最后才是腳本; 2、模塊特點:問題嚴重度在同一層級,代碼的分析順序。首先,同一層級的代碼,應該優(yōu)先分析重要的模塊,重要模塊是核心功能處理、調(diào)用頻繁度高的模塊;其次,出現(xiàn)問題的模塊會隱藏更多的問題,分析那些已經(jīng)發(fā)現(xiàn)過問題的模塊,可能會發(fā)現(xiàn)更多的問題。 通過查看工程文件或工程屬性,明確源代碼將會被編譯成xxx名稱的.xxx擴展名的文件。代碼最終都會被編成.xxx后綴的目標文件,在開始梳理具體實現(xiàn)前,要明確目標文件的名稱和運行方式。 確定了模塊名后,緊接著要確定多個相互作用的模塊的調(diào)用關(guān)系。從當前代碼所在的模塊出發(fā)梳理調(diào)用關(guān)系,首先需要明確調(diào)用關(guān)系是兩個以上模塊之間的關(guān)系,也就是在調(diào)用過程中,存在調(diào)用方和被調(diào)用方;其次,當前代碼所在模塊在實際調(diào)用中,可能會調(diào)用其它模塊,也可能被其它模塊調(diào)用。 1、哪些模塊調(diào)用了當前模塊:在整個業(yè)務代碼范圍內(nèi)搜索當前模塊的模塊全名,包含.xxx后綴,就能找出該模塊被哪些模塊調(diào)用。 2、當前模塊調(diào)用了哪些模塊:在該模塊的工程文件中,搜索和分析會和其他模塊發(fā)生調(diào)用關(guān)系的關(guān)鍵詞,這里的關(guān)鍵詞一般是加載函數(shù)的名字、文件后綴名等,即可找到該模塊調(diào)用了哪些模塊。 在當前模塊被調(diào)用和調(diào)用其他模塊的語句處,即步驟三中發(fā)生調(diào)用關(guān)系的語句處,找到模塊間起連接作用的接口。具體接口的形式、分類和使用方法依賴于平臺和代碼開發(fā)語言,確定接口的準則會有所不同,這里不贅述,大家可根據(jù)自己實際分析的代碼確定接口。 從接口入手梳理代碼結(jié)構(gòu)關(guān)系首先要做兩件事: ①查看該接口類的定義和各個方法名稱,明確該接口具備什么方法。接口類一般都是抽象類,抽象類里的方法一般沒有具體實現(xiàn); ②找接口類的實現(xiàn)類,即搜索public 接口類名,找到該接口的實現(xiàn)類,從實現(xiàn)類的各個方法定義中開始分析各個方法的具體實現(xiàn)邏輯,這些實現(xiàn)過程可能包含對其他模塊的調(diào)用、對其他接口/類的調(diào)用、對本接口其他方法的調(diào)用。 從接口入手開始分析代碼的依據(jù)是類關(guān)系的強弱性,先簡單說明UML給出的六種類關(guān)系的強弱順序:泛化=實現(xiàn)>組合>聚合>關(guān)聯(lián)>依賴。泛化和實現(xiàn)關(guān)系像是代碼結(jié)構(gòu)的縱向關(guān)系,我們理清實現(xiàn)和泛化關(guān)系,就等同于找到房子的承重墻;組合、聚合和關(guān)聯(lián)更能代表代碼結(jié)構(gòu)的橫向關(guān)系,是具體實現(xiàn)過程;依賴關(guān)系也是一種代碼結(jié)構(gòu)的橫向關(guān)系,但是這種關(guān)系比較弱,即對代碼的整個框架影響較小,所以分析時不建議花費太多時間。 在逐步分析確定具體實現(xiàn)邏輯的過程中,有以下幾個關(guān)注點: ①從需求的功能出發(fā),確定功能是否如期實現(xiàn)、代碼中是否有分支遺漏、各種異常處理是否完備; ②從CodeReview角度出發(fā),查找代碼中的基本缺陷,如變量沒有初始化、資源使用完后未釋放、函數(shù)返回值出錯等; ③從代碼實現(xiàn)結(jié)構(gòu)出發(fā),確定設計的接口是否合理、多線程流程是否恰當、架構(gòu)是否清晰等。 以下將按照上文闡述的”五步法“,以Windows系統(tǒng)Visual Studio下C++代碼為例,進行實戰(zhàn)演練。 例如分析的項目包含C++代碼和Lua代碼,從風險控制的角度出發(fā),優(yōu)先選擇C++代碼進行分析。 這時將會遇到另一個問題:模塊關(guān)系是怎么樣的?具體實現(xiàn)邏輯是怎么樣的? 打開.vcproj工程文件,查看ConfigurationType值(2是dll文件,1是exe文件),或者VS里面查看工程屬性Properties-ConfigurationType就可以看出該文件的生成文件名(xxx.xxx),dllTest.vcproj最后編譯出來的二進制文件是dllTest.dll。 1、哪些模塊調(diào)用了當前模塊:在整個解決方案文件(.sln)中搜索模塊名,可看出哪些模塊調(diào)用了該模塊,dllCall\dllCall.cpp所在模塊dllCall.exe(步驟二的方法獲取模塊名)會調(diào)用dllTest.dll。 2、當前模塊調(diào)用了哪些模塊:在.vcproj工程文件中搜.lib、LoadLibrary、CoCreateInstance,可以看出dllCall.exe包含shell32.lib等靜態(tài)庫。
dllCall.exe會通過CoCreateInstance調(diào)用dllCom.dll等動態(tài)庫。 除了通過搜索關(guān)鍵字的方式獲取模塊關(guān)系之外,還可以通過調(diào)試和depends.exe等工具來理清楚模塊間的依賴關(guān)系。 C++接口一般分為COM接口和LoadLibrary導出接口兩類,其中LoadLibrary導出接口的使用方法又細分為兩種: ①COM接口,CoCreateInstance的第四個參數(shù)就是接口的IID,IID_后直接跟的就是接口類的名稱; ②LoadLibrary導出接口,GetProcAddress的第二個參數(shù)就是接口函數(shù):a.該接口函數(shù)直接被使用;查找該接口函數(shù)的定義,如下add是接口函數(shù)。
上例接口ICacheClient的實現(xiàn)類是CCacheClient。 CCacheClient的方法和屬性如下: 組合、聚合和關(guān)聯(lián)關(guān)系的代碼在整個工程中占比非常大,是代碼分析比較耗時的過程,我們可以借助工具輔助分析,推薦代碼閱讀分析工具Understand。 UnderStand集成了代碼編輯器、代碼跟蹤器和代碼分析器,支持C/C++/C#, Ada, Java, FORTRAN, Delphi和Jovial等語言,并且具有強大的界面,能將分析結(jié)果以圖表、圖形等形式呈現(xiàn)給大家,如下實例圖依次為Understand生成的類關(guān)系圖、函數(shù)調(diào)用關(guān)系圖和數(shù)據(jù)流圖。 最后,那么對于我們來說,要在有限時間內(nèi)完成高質(zhì)量的代碼分析,才能更好的適應“迭代節(jié)奏快、需求多、測試時間短、代碼量大”等現(xiàn)狀、更好的打擊和消滅bug。希望以上代碼分析的方法能為大家提供一些思路,能幫助大家提升一些代碼分析效率。 讀者互動環(huán)節(jié) 大家日常工作中是如何做代碼分析的?上述5步法,在你的項目中是否也有用武之地? 精彩的回答除了可以移入精選留言,還有機會獲取TMQ提供的精美禮品一份~ 期待您的回答哦 尋人啟示 獲獎名單 獲獎者微信ID:Friday |
|