1. 程式人生 > >程式碼審計--18--PMD詳細

程式碼審計--18--PMD詳細

2.1 PMD概述

1、工具說明

PMD是一個程式碼檢查工具,它用於分析Java原始碼,找出潛在的問題。

PMD的核心是JavaCC解析器生成器。PMD附帶了16個可以直接使用的規則信,利用這些規則可以找出Java源程式的許多問題;此外,PMD規則是可以定製的,可以新增新規則:通過編寫Java程式碼並重新編譯PDM,或者更簡單些,編寫XPath表示式,它會針對每個Java類的抽象語法樹進行處理,檢查Java程式碼是否符合某些特定的編碼規範。

PMD已經與JDeveloper、Eclipse、jEdit、JBuilder、BlueJ、CodeGuide、NetBeans、Sun Java Studio Enterprise/Creator、IntelliJIDEA、TextPad、Maven、Ant、Gel、JCreator以及Emacs整合在一起。

2、PMD基本功能

利用PMD進行靜態程式碼檢測可以找出Java源程式的以下問題:

潛在的bug:空的try/catch/finally/switch語句。 未使用的程式碼:未使用的區域性變數、引數、私有方法等。 可選的程式碼:String/StringBuffer的濫用。 複雜的表示式:不必要的if語句、可以使用while迴圈完成的for迴圈。 重複的程式碼:拷貝/貼上程式碼意味著拷貝/貼上bugs。 迴圈體建立新物件:儘量不要在for或while迴圈體內例項化一個新物件。 資源關閉:Connect,Result,Statement等使用之後確保關閉掉。

2.2 使用PMD

1、開始執行

PMD 是用 Java 程式語言編寫的,並且要求使用 JDK 1.3 或更高的版本。如果使用命令列,那麼 PMD 的安裝和執行會非常簡單。

執行 PMD 最簡單的方法是呼叫指令碼 pmd.sh(在 Unix/Linux 上)或指令碼 pmd.bat(在 Windows 上)。不太合常規的是,這些指令碼在 pmd-2.1/etc 目錄中,而不是在 bin 目錄中。這個指令碼採用了三個命令列引數:

要檢查的 .java 檔案的路徑。 指定輸出格式的關鍵字 html 或 xml。 要執行的規則集的名稱。

以下命令使用命名規則集檢查 ImageGrabber.java 檔案並生成 XML 輸出

$ /usr/pmd-2.1/etc/pmd.sh ImageGrabber.java xml rulesets/naming.xml

2、審計結果檢視分析

預設報告 預設情況下,結果輸出類似於以下報告,這些輸出被髮送到 System.out:

PMD 的 XML 報告

<?xml version="1.0"?>
<pmd>
	<file name="/Users/elharo/src/ImageGrabber.java">
		<violation line="32" rule="ShortVariable"  ruleset="Naming Rules" priority="3">
			Avoid variables with short names like j
		</violation>

		<violation line="105" rule="VariableNamingConventionsRule"  ruleset="Naming Rules" priority="1">
			Variables that are not final should not contain underscores 
		</violation>
	</file>
	<error filename="/Users/elharo/src/ImageGrabber.java"  msg="Error while processing /Users/elharo/ImageGrabber.java"/>
</pmd>

在上面的例子中可以看到,PMD 發現了兩個問題:在 ImageGrabber.java 有一個短變數名稱,名稱中有一個下劃線。這些看起來可能是小問題,但是經過仔細考察,可以完全排除 j 變數,因為它與另外一個單獨遞增變數的功能相同。

HTML報告

可以把 PMD 的輸出重定向到檔案中、或者通過管道,以常見的方式將它傳遞到編輯器中。通常HTML 格式的報告更為直觀的展示了審計結果

在這裡插入圖片描述

在檢查原始碼樹時,把結果輸出到檔案中會非常有幫助。當結果中生成誤報(false positive)的時候,可以很容易地從檔案中認出並刪除最常見的誤報,因為它們通常非常相似;然後您就可以解決其餘的問題。

PMD 中惟一缺乏的特性就是不能向原始碼中新增 “lint 註釋”,以便對要執行的一些明顯有危險的操作進行提醒。

3、PMD內建規則集

基本(rulesets/basic.xml)—— 規則的一個基本合集,可能大多數開發人員都不認同它: catch 塊不該為空,無論何時重寫 equals(),都要重寫 hashCode(),等等。

命名(rulesets/naming.xml)—— 對標準 Java 命令規範的測試:變數名稱不應太短;方法名稱不應過長;類名稱應當以小寫字母開頭;方法和欄位名應當以小寫字母開頭,等等。

未使用的程式碼(rulesets/unusedcode.xml)—— 查詢從未使用的私有欄位和本地變數、執行不到的語句、從未呼叫的私有方法,等等。

設計(rulesets/design.xml)—— 檢查各種設計良好的原則,例如: switch 語句應當有 default 塊,應當避免深度巢狀的 if 塊,不應當給引數重新賦值,不應該對 double 值進行相等比較。

匯入語句(rulesets/imports.xml)—— 檢查 import 語句的問題,比如同一個類被匯入兩次或者被匯入 java.lang 的類中。

JUnit 測試(rulesets/junit.xml)—— 查詢測試用例和測試方法的特定問題,例如方法名稱的正確拼寫,以及 suite() 方法是不是 static 和 public。

字串(rulesets/string.xml)—— 找出處理字串時遇到的常見問題,例如重複的字串標量,呼叫 String 建構函式,對 String 變數呼叫 toString() 方法。

括號(rulesets/braces.xml)—— 檢查 for、 if、 while 和 else 語句是否使用了括號。

程式碼尺寸(rulesets/codesize.xml)—— 測試過長的方法、有太多方法的類以及重構方面的類似問題。

Javabean(rulesets/javabeans.xml)—— 檢視 JavaBean 元件是否違反 JavaBean 編碼規範,比如沒有序列化的 bean 類。

終結函式(finalizer)—— 因為在 Java 語言中, finalize() 方法不是那麼普遍,所以它們的使用規則雖然很詳細,但是人們對它們相對不是很熟悉。這類檢查查詢 finalize() 方法的各種問題,例如空的終結函式,呼叫其他方法的 finalize() 方法,對 finalize() 的顯式呼叫,等等。

克隆(rulesets/clone.xml)—— 用於 clone() 方法的新規則。凡是重寫 clone() 方法的類都必須實現 Cloneable, clone() 方法應該呼叫 super.clone(),而 clone() 方法應該宣告丟擲 CloneNotSupportedException 異常,即使實際上沒有丟擲異常,也要如此。

耦合(rulesets/coupling.xml)—— 查詢類之間過度耦合的跡象,比如匯入內容太多;在超型別或介面就已經夠用的時候使用子類的型別;類中的欄位、變數和返回型別過多等。

嚴格的異常(rulesets/strictexception.xml)—— 針對異常的測試:不應該宣告該方法而丟擲 java.lang.Exception 異常,不應當將異常用於流控制,不應該捕獲 Throwable,等等。

有爭議的(rulesets/controversial.xml)—— PMD 的有些規則是有能力的 Java 程式設計師可以接受的。但還是有一些爭議。這個規則集包含一些更有問題的檢驗,其中包括把 null 賦值給變數、方法中有多個返回點,以及從 sun 包匯入等。

日誌(rulesets/logging-java.xml)—— 查詢 java.util.logging.Logger 的不當使用,包括非終狀態(nonfinal)、非靜態的記錄器,以及在一個類中有多個記錄器。

4、構建規則集

當需要頻繁地用某個規則集合進行檢查,可以把這些規則組合在規則集檔案中,如下例子所示,這個規則集匯入了一些基本規則、命名規則和設計規則:

匯入基本規則、命名規則和設計規則的規則集

<?xml version="1.0"?>
<ruleset name="customruleset">
	<description>
		Sample ruleset for developerWorks article
	</description>
<rule ref="rulesets/design.xml"/>

<ruleref="rulesets/naming.xml"/>
	<rule ref="rulesets/basic.xml"/>
</ruleset>

可以從每個規則集中選取每個想要包含的規則。下面的例子顯示了一個定製規則集,它從三個內建規則集中選取了 11 個特定的規則。因為檢查大型的程式碼基址需要的時間相當長,即使是在快速硬體上也是如此,所以這種方法可以更快地發現要查詢的特定問題。

匯入 11 個特定規則的規則集

<?xml version="1.0"?>
<ruleset name="specific rules">
	<description>
		Sample ruleset for developerWorks article
	</description>

	<rule ref="rulesets/design.xml/AvoidReassigningParametersRule"/>
	<rule ref= "rulesets/design.xml/ConstructorCallsOverridableMethod"/>
	<rule ref="rulesets/design.xml/FinalFieldCouldBeStatic"/>
	<rule ref="rulesets/design.xml/DefaultLabelNotLastInSwitchStmt"/>
	<rule ref="rulesets/naming.xml/LongVariable"/>
	<rule ref="rulesets/naming.xml/ShortMethodName"/>
	<rule ref="rulesets/naming.xml/VariableNamingConventions"/>
	<rule ref="rulesets/naming.xml/MethodNamingConventions"/>
	<rule ref="rulesets/naming.xml/ClassNamingConventions"/>
	<rule ref="rulesets/basic.xml/EmptyCatchBlock"/>
	<rule ref="rulesets/basic.xml/EmptyFinallyBlock"/>
</ruleset>

可以把大多數規則包含在一個集合,但是排除不需要的和會造成大量誤報的特定規則。例如,XOM 在進行表查詢的時候經常使用沒有 default 塊的switch 語句。可以保留大多數設計規則,但是可以在匯入設計規則的規則元素中新增<exclude name="SwitchStmtsShouldHaveDefault"/>子元素,避開對遺漏default 塊的檢查,如下例子 所示:

排除了設計規則的規則集

<?xml version="1.0"?>
<ruleset name="dW rules">
	<description>
		Sample ruleset for developerWorks article
	</description>

	<rule ref="rulesets/design.xml">
		<exclude name="SwitchStmtsShouldHaveDefault"/>
	</rule>
</ruleset>

可用的規則並不僅限於內建規則,可以新增新規則:可以通過編寫 Java 程式碼並重新編譯 PDM,或者更簡單些,編寫 XPath 表示式,它會針對每個 Java 類的抽象語法樹進行處理。