搜尋此網誌

2009年9月29日 星期二

[工具介紹] Java程式檢測工具 (一) - PMD

前言

PMD 是來執行 Java 程式檢測的工具,檢測的對象是原始程式碼。雖然 PMD 看起來像是一個縮寫字,但是作者表示當初採用這個字純粹只是因為這幾個單字念起來很順口。根據網站上面的介紹, PMD 可以找出下列問題:

  • 疑似的臭蟲 (possible bugs),例如空的 try/catch/finally/switch 描述句。
  • 無法執行的程式碼  (dead code),例如未使用的 local 變數、參數、或是 private 函數。
  • 不良的程式碼 (suboptimal code),例如效率不良的使用 String/StringBuffer
  • 過於複雜的語法 (overcomplicated expressions),例如不必要的 if 描述句,可以用 while 迴圈取代的 for 迴圈。
  • 重複的程式碼 (duplicated code),剪貼的程式碼表示臭蟲也被複製了。

PMD 本身沒有提供圖形化的介面,而必須透過命令列的方式加以操作。當然在支援 Ant 方面, PMD 是沒有問題的。除此之外,更支援 Maven 1/2 的開發環境。而對於多數程式開發人員關心的 IDE 支援度方面,幾乎市面上主要的 IDE 都受其支援。以此看來,要在大多數的開發環境下使用 PMD 幾乎是沒有甚麼門檻。

接下來我就以 Eclipse 的 Plug-in 使用方式作為說明範例。

安裝 Eclipse Plug-in

  1. 選取功能選單的 Help –> Install New Softwarepmd 001
  2. 點選“Add…” 按鈕,輸入下列資料後按下按鈕“ OK”。pmd 002
  3. Work with 的下拉選項中選取  PMD – http://pmd.sf.net/eclipse  ,點選後需要等待一段時間。
  4. 點選 PMD for Eclipse 3(使用 Eclipse 2 的人請選擇 PMD for Eclipse 2)後按下按鈕“Next > “ ,點選後需要等待一段時間。pmd 003
  5. 選取  PMD Plug-in 後按下按鈕 “Next > “。pmd 004
  6. 選取  I accept the terms of the license agreement 後按下按鈕 “Finish”。pmd 005
  7. 等待安裝畫面執行完畢。pmd 006
  8. 安裝完畢後按下 “Yes” 按鈕重開 Eclipse 以使 PMD Plug-in 生效。pmd 007

使用 PMD 檢測 Struts 程式碼

  1. 在程式碼的目錄下使用滑鼠右鍵叫出功能選單,選取  PMD –> Check Code with PMDpmd 008
  2. 等候 PMD 檢測完畢。pmd 009
  3. 檢測完畢後會多出兩個視窗,分別為 Violations Overview 與 Violations Outline。
  4. Violations Overview 視窗內容為各 package 與各檔案所產生的違規事件 (Violations) 數量統計,其中 Violations/LOC 與 Violations/Method 數據必須先點選該 package 後才會產生(或點選Calculate statistics按鈕,此按鈕位於五個不同顏色按鈕的左方)。此外可以透過右上方五個不同顏色的按鈕,選擇只統計特定等級的違規事件。PMD 將違規事件依重要性依序分為 error high, error, warning high, warning, information 五種等級。 pmd violations overview
  5. Violations Outline 則依據使用者所點選的檔案顯示該檔案的各項違規,圖示範例為 org.apache.struts2 下的 ServletActionContext.java。點選特定一筆資料後會自動跳到該行程式碼。pmd violations outline
  6. 比較 ServletActionContext.java 在使用 PMD 前後可以發現警告的數量從原先的 3 個變成了 11 個,其中還包含 1 個錯誤 (第 42 行)。

    使用 PMD 前的警告資訊

    servlet action context without pmd

    使用 PMD 後的警告資訊,其中第 42 行產生錯誤。而第 50 行雖同時被 Eclipse 與 PMD 標示為警告(違規),但其警告原因卻不相同servlet action context with pmd
  7. 查看 org.apache.struts2.components 下的 AppendIterator.java ,我們發現第 125 行雖然同時被 Eclipse 與 PMD 標示出來,但是不但標示原因不同,連標示等級都不一樣。PMD 將 125 行標示為錯誤。 append iterator with pmd

設定 PMD 的檢測項目

經由上述的實際範例中我們可以看到透過 PMD 的檢測將會發現許多違規事件,但是當我們進一步加以檢視後可以發現其中不少違規事件其實跟錯誤沒有關係。為了避免過多無謂的違規事件影響我們發現真正的問題,我們可以透過下列幾個方法。

  1. 利用前述 Violations Overview 的功能,僅顯示特定等級的違規事件。
  2. 第一種方法雖然方便,但是其僅能影響統計數據,包含 Violations Outline 與程式碼的警告訊息都不會消失。要讓這些資訊消失的方法就是設定 PMD ,修改其檢測的項目。
    1. 點選 Window –> Preferences
    2. 點選 PMD –> Rules Configuration
    3. 點選想要修改或刪除的規則後,點選按鈕 ”Edit rule…” 或是  ”Remove rule”。
    4. 需要特別注意的是如果要 PMD 不檢測特定的規則,必須直接將規則刪除,而沒有辦法告知 PMD 跳過此一檢測規則。因此最好能夠在進行動作前做好規則的備份 (Export rule set…),以免日後要重新檢測該規則時已經找不到了。
    5. 透過此一畫面也可以進行規則的新增。
    6. 設定完成後按下按鈕 “OK”。
    7. 在程式碼目錄上點選滑鼠右鍵叫出功能選單,點選 PMD –> Clear PMD Violations 將之前的違規訊息加以清除。
    8. 在程式碼目錄上點選滑鼠右鍵叫出功能選單,點選 PMD –> Check Code with PMD ,以便依據新的規則重新進行檢測。
  3. 雖然第二種做法可以減少很多看似不重要的警告訊息,但是也有可能因此將疑似的問題隱藏起來。之間的取捨,就需要仔細的加以評估。例如我們看到 PMD 在 org.apache.struts2.views.xslt 下的 StringAdapter.java 標示出第 78 行的 node 變數連續被指定兩次,是一項違規事件,而等級是最不嚴重的 information 。我們透過實際檢閱程式後,發現此處用法的結果是我們想要的。但是在某些時候,這樣的用法可能代表了程式有漏寫或是其它的錯誤。如果我們因為認為這種情況不重要,甚至是認為 information 等級的違規事件不重要而將此規則刪除,可能就真的失去了找到問題的機會。 比較好的做法不是直接刪除檢測的規則,而是採用 Supressing Warning 的機制。

    node 變數連續被指定兩次的行為,被 PMD 標示為違規事件
    string adapter

違規事件的處理

透過 PMD 找出違規事件後,我們需要先決定是否要修正特定的違規事件。判斷的依據除了根據程式本身決定是否違規事件將會產生問題外(這些通常是所謂誤判),有更多時候是必須依據自我程式開發的原則。原因在於這些違規事件很多是因為沒有遵守所謂的良好寫作習慣所產生,但是這些良好的寫作習慣卻不見得在任何時候都可以適用。當然我們還是希望在大多數的情況下能夠盡量加以遵守這些習慣,以免日後產生一些”很奇怪”的問題。此外,除非你針對良好的程式寫作方式做過訓練,不然大多數的違規事件對一般程式設計師來說可能不容易馬上理解。針對此一問題,在  Violations Outline 的畫面中可以在特定的錯誤訊息上點選滑鼠右鍵叫出功能選單,此時有一個 Show Details 的選項,點選後會出現違規事件的說明。透過此畫面可以讓我們知道發生違規的原因,如此一來就能幫助我們決定是否要修改此一違規事件,以及如何加以修改。show details

除了進行修改外,有時候我們的決定是不進行修正。此時我們必須透過 Suppressing Warnings 的機制,告訴 PMD (以及其他團隊成員) 此一違規事件不需要再加以提示。操作方式為透過  Violations Outline 的畫面在特定的錯誤訊息上點選滑鼠右鍵叫出功能選單,之後選取 Mark as reviewed 選項就可以讓此一違規事件加以排除。如果我們注意看程式碼的部分,會發現 PMD 在發生違規事件的程式碼後面加上了 // NOPMD by xxxxx xxxx 之類的字樣。這是 PMD 用來 Suppressing Warnings 的方式之一,另外也可以透過 Annotation 的方式來達到同樣的目的,只可惜透過 Eclipse 的 Plug-in 無法使用 Annotation 的方式。而使用 Plug-in 的最大問題在於它不會強迫程式設計師輸入原因加以說明,所以將會造成日後維護上的極大困擾,此部分必須靠程式設計師的自制能力加以彌補。violations menu

當我們將違規事件標示為 reviewed 之後,並不會馬上從 Violations outline 畫面中消失。不過當日後再次進行檢測時,這些已經 reviewed 過的違規事件就不會再出現了。

找出重複的程式碼

重複的程式碼一直以來是一個很嚴重的問題。我所謂的嚴重不只在於這是一個很常見的問題,更在於其所衍生後續維護的問題之嚴重性。 其實這個問題很容易了解,要避免的方法也很簡單,但是我相信即使在未來依舊很難加以消除,因為貪圖方便(剪貼)是大多數人的自然天性。 PMD 有一個比較少見的功能,就是可以找出疑似重複的程式碼。操作方式如下:

  1. 操作方式跟之前有所不同,必須在專案上(而不是程式碼目錄上)點選滑鼠右鍵叫出功能選單,然後透過 PMD –> Find Suspect Cut And Paste 的選項加以進行。cdp invoke
  2. 設定參數主要只有一個,那就是多少行程式碼重複才列入考量 (Minimum Tile-size)。設定好之後,按下按鈕 “OK” 就會進行檢測的工作。cpd setting
  3. 檢測完畢後結果會出現在 CPD View,比較可惜的是此一介面並沒有辦法直接連到程式碼本身,所以如果要進行判斷或修正時需要額外多一些操作步驟。cpd view
  4. 除了 CDP View 之外, PMD 也可以將檢測結果產生於報表中。其預設格式為 txt ,檔案位置則為 reports / cpd-report.txt 。此檔案內容包含重覆程式碼所在的檔案名稱與程式碼行數,另外也包含重覆程式碼的內容。不過因為重複程式碼本身沒有前後文,所以要做為是否需要進行修正的判斷依據有時候仍嫌不足。cpd report 

產生報表

PMD 除了可以產生重複程式碼的報表外,也可以產生違規事件的報表。同樣必須透過在專案上點選滑鼠右鍵叫出功能選單,然後利用 PMD –> Generate reports 的選項加以進行。執行完畢後會在 reports 目錄下產生多個檔案,其中包含 html, txt 與 csv 格式,以方便日後的使用。

JSP支援

雖然 PMD 支援 JSP 的檢測,但是卻無法檢測 JSP 內的 Java 程式碼 (Java Scriptlets)。此外,EL的語法也是無法進行檢測。

Rulesets

PMD 將檢測規則依據屬性與檢測對象的不同,區分為多個 ruleset 。在安裝 Eclipse 的 Plug-in 之後,幾乎所有相關的 ruleset 就已經設定完畢。但是除了這些預設的 ruleset 之外,PMD 也提供了其他的 ruleset (例如給 Android 使用的 ruleset)以備所需的人加以利用。檢視過這些 ruleset 後,我們發現只有一個 ruleset 是跟安全直接相關,名稱為 Sun Security ,而內容只有兩個檢測規則。PMD 是一個不錯的工具,只可惜對於程式安全部份的幫助還是極其有限,期待日後在這部分會有加強的計劃。

結語

PMD 算是一個相當成熟的工具,雖然其針對程式碼的檢測規則還是多屬於良好寫作習慣的建議,而不是針對網站程式的安全,但是卻不能因此就加以忽視。如果以廣義的安全來看,穩定的系統也是安全的一項需求,而穩定來自於寫作良好的程式碼(還有其它因素)。此外,這類工具除了需要熟悉操作外,更重要的是藉此培養程式設計師良好的寫作觀念與能力,這點 PMD 倒是可以提供相當不錯的協助。

About