程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 使用 IBM 靜態工具優化 Java 代碼,第 1 部分: 工具入門

使用 IBM 靜態工具優化 Java 代碼,第 1 部分: 工具入門

編輯:關於JAVA

什麼是 BEAM?

關於縮寫 BEAM 的聲明

處於表達簡潔的目的,本文采用了工具名稱的縮寫 — BEAM,這只是工具全稱 “Checking Tool for Bugs Errors and Mistakes”的文字縮寫,而不是工具的名稱。

IBM Checking Tool for Bugs Errors and Mistakes(本文後面將采用其文字縮寫 BEAM )是 IBM 開發的一個靜態分析工具,可以用於分析並查找出 C, C++ 和 Java 代碼中的一 些不容易發現的潛在錯誤,從而達到提高代碼質量的目的。同動態分析工具和其它靜態分析 工具相比,它擁有一些可貴的特性。

同動態分析工具的比較

首先,BEAM 可以直接對代碼進行分析,不需要運行代碼,也不需要對代碼編譯鏈接,所 以相對容易。比如,運行它不需要為代碼編寫任何測試用例,而動態分析僅在單元測試時就 需要大量測試用例,而且這些測試用例只能測試單個代碼片段,重用性很低,基本上每個類 都需要不同的測試用例,因此只有編寫足夠多的單元測試用例才能測試大型的軟件產品,耗 時且代價高昂。

其次,這個工具可以查找出單元測試和專業代碼審查所可能錯過的代碼缺陷和安全弱點 ,比如內存洩漏,非法的數據庫訪問和非法內存訪問等等,據統計,BEAM 可以在平均每 1000 行已經經過測試的代碼中找出一個錯誤。

再次,在開發早期就可以運行其對代碼進行檢查,從而在產品開發早期發現缺陷,有助 於降低開發成本。同時,它還有助於開發人員在產品開發早期發現自己編碼風格的缺點,及 早做出改進,防止工程後期重復發生相同類型的錯誤。

同其它靜態分析工具的比較

同其它靜態分析工具一樣,BEAM 也是對代碼進行語法掃描,通過算法對代碼進行檢查分 析,並和一些 bug 模式進行比較,最終標明問題區域,輸出分析結果。但是相對於其他靜 態分析工具,它有一些獨到的優點。

首先,它模仿 javac 的使用,語法和 javac 相似,支持許多 javac 的常用命令參數, 而且具有相同的語義,比如 -classpath,-source,-d 等等;不僅如此,它還模仿 javac 接受相同的源文件,只不過不是編譯,而是分析檢查這些源文件。這樣習慣於 javac 的開 發人員就可以很輕松的學會使用。

其次,許多靜態分析工具報錯的准確性較低,很多被這些工具標記為錯誤的代碼事實上 是正確的,這樣會增加程序員工作量,並有可能掩蓋真正的錯誤。而 BEAM 使用了額外的定 理證明(theorem proving)技術來判斷一個潛在的錯誤是否是真正的錯誤,從而減輕了程 序員判斷錯誤真偽所需的工作量。例如:

清單 1. 代碼示例

int Average(int Sum, int N)
  {
    return Sum / N;
  }

很多靜態分析工具遇到清單 1 的代碼會報除數為 0 的錯誤,而事實上如果清單 1 所在 的整個代碼工程中,進入 Average 的參數 N 永遠都不可能為 0 的話,這個錯誤就不能算 是真正的錯誤。而 BEAM 只有在發現了確鑿的證據證明除數 N 可能為 0 時才會報錯。例如 清單 2 和清單 3,都會報除數為 0 的錯誤。

清單 2. 報錯代碼示例 1

int Average(int Sum, int N)
  {
    if ( N == 0 ) // 這裡證明 N 有可能等於 0
    X = 1;

    return Sum / N;
  }

清單 3. 報錯代碼示例 2

int Average(int Sum, int N)
  {
    if ( N != 0 ) // 這裡證明 N 有可能允許等於 0
    X = 1;

    return Sum / N;
  }

再次,這個工具使用了符號執行(symbolic execution)技術指出導致錯誤發生的條件 ,並在輸出結果中打印出整個出錯路徑供程序員分析。例如清單 4:

清單 4. 輸出結果示例

BEAM_VERSION=3.4.0
BEAM_ROOT=D:\Work\class\beam
BEAM_DIRECTORY_WRITE_INNOCENTS=D:\Work\class\beam\beam_data\innocent1
BEAM_DIRECTORY_WRITE_ERRORS=D:\Work\class\beam\beam_data\errors1

-- ERROR2   /*operating on NULL*/    >>>ERROR2_foo_55479ec82b606
"Example.java", line 11: invalid operation involving NULL pointer
ONE POSSIBLE PATH LEADING TO THE ERROR:
"Example.java", line 2: loop entry condition is true
"Example.java", line 5: the if-condition is true
"Example.java",line 8: the if-condition is true(used as evidence that  error is possible)
"Example.java", line 11: using operation `[]' to dereference NULL  pointer `p'

VALUES AT THE END OF THE PATH:
p = 0

配置並運行

BEAM 目前多用於 Linux/AIX 平台上對 C 和 C++ 語言的檢查分析,而在最常用的 Windows 平台上使用其對 Java 進行靜態分析的人不多,因此經驗不足,文檔匮乏。本文接 下來就介紹如何在 Windows 平台上運行這個工具對 Java 代碼進行分析。

與大多數工具一樣,BEAM 可以以多種方式運行 —— 命令行、使用 Ant 或作為 Eclipse 插件程序。本文將只介紹使用 Ant 運行。

安裝 BEAM

BEAM 當前最新版本是 3.4.2,而且支持 Windows 2000 及其以上版本。本文假設您將其 安裝到 C:\BEAM-3.4.2 下。

下載並安裝 ActivePerl

在這個工具的 bin 目錄下有些 Perl 腳本(比如 beam_configure),所以需要 5.004 或以上版本的 Perl 解釋器去解釋。本文假設您把 ActivePerl 安裝到 C:\Perl 目錄下。

運行 beam_configure 腳本生成 Java 編譯器配置文件

這個工具是為了盡可能多地匹配本地 Java 編譯器 javac 的行為,需要通過一個 TCL 格式的編譯器配置文件去了解本地 javac 的環境。這個編譯器配置文件中包括當前 Java 語言的版本(如 1.4),默認的根類路徑(如 jre\lib\core.jar)和默認的 Classpath 等 信息。

TCL 格式的編譯器配置文件是通過 Perl 腳本 beam_configure 檢查本地 Java 編譯器 javac 後,使用檢查結果自動生成的,如清單 5:

清單 5. 生成 Java 編譯器配置文件

C:\Perl\bin\perl
    "C:\BEAM-3.4.2\bin\beam_configure"
    --java D:\Tools\Work\ibm-sdk\bin\javac
    -o my_config.tcl

C:\Perl\bin\perl:Perl 解釋器的絕對路徑。

C:\BEAM-3.4.2\bin\beam_configure:Perl 腳本 beam_configure 的絕對路徑。

--java:表明接下來的編譯器是 Java 編譯器。

D:\Tools\Work\ibm-sdk\bin\javac:本地 Java 編譯器 javac 的絕對路徑。

-o:是 output 的意思,表明將配置信息輸出到接下來指定的文件中。

my_config.tcl 就是最終生成的 Java 編譯器配置文件,名字可以任意,但是由於接下 來要使用,所以需要記住這個名字。運行完此命令,當前目錄下就生成了 my_config.tcl。 本文假設您把 my_config.tcl 放在 D:\Work\Beam\Java\ 下。

注意:需要修改 beam_configure 腳本中的一行代碼才能成功運行生成 TCL 文件。

my $tmpdir = "/tmp/beam_config.$$." . int(rand  1000000);

改成:

my $tmpdir = "c:/temp/beam_config.$$." .  int(rand 1000000);

因為 bin 目錄下的 beam_configure 是針對 Linux 和 AIX 平台的,所以臨時目錄是 /tmp,而 Windows 平台的臨時目錄是 c:/temp,所以需要修改臨時目錄。

下載並安裝 Ant

這個工具支持 Ant 運行,可以把其當作 Ant 的一個任務來執行。本文假設您把 Ant 安 裝到 C:\apache-ant-1.7.0 下。

創建支持 BEAM 的 build.xml 文件

在 Java 源文件的根目錄下為 Ant 創建 build.xml。

1. 指定 XML 版本

所有的 XML 文件的第一行都必須是一個 XML 聲明,指定將要使用的 XML 版本,本文使 用 XML 1.0 版本。如清單 6

清單 6. 指定 XML 版本

<?xml version="1.0"?>

2. 指定根目錄

定義屬性 beam.install,指定根目錄,如清單 7 。通常,beam_compile 程序應該位於 ${beam.install}/bin 下。

清單 7. 指定根目錄

<property name="beam.install" value="C:\BEAM -3.4.2"/>

3. 指定任務名

因為要將其作為 Ant 的一個任務來運行,所以接下來需要指定任務 taskdef,只有定義 過 taskdef,才可以在運行 Ant 時通過指定任務名來運行指定的任務。

清單 8. 指定任務名

<taskdef name="beam"
    classname="com.ibm.beam.ant.BeamTask"
    classpath="${beam.install}/jar/ant-beam.jar" />

taskdef name="beam":指定任務名為 beam,接下來 Ant 運行時可以通過指定 beam 來 運行指定的任務。

classname="com.ibm.beam.ant.BeamTask":指定接下來要執行 beam 任務類的全限定名 ,告訴 Ant 運行 beam 任務要裝載哪一個類。

classpath="${beam.install}/jar/ant-beam.jar":指定 classpath,供尋找 classname 時使用。

4. 指定 Java 源代碼路徑

清單 9. 指定 Java 源代碼路徑

<property name="code.dir"  value="./java"/>

指定 code.dir 屬性,表明當前路徑的 java 子目錄下存放的所有 Java 源文件代碼都 需要這個工具進行分析。

5. 指定輸出結果路徑

清單 10. 指定輸出結果路徑

<property name="beam.classes"  value="./class/beam"/>

如清單 10 所示,beam.classes 存放分析輸出結果(如:BEAM-messages 文件)。

6. 定義 path

BEAM 需要 javac 編譯代碼時使用的 classpath,目的是檢查源代碼所調用的類庫文件 是否在 classpath 內。這裡定義具有 id 屬性的 path 元素,供接下來其 運行時指定 classpath 用。

清單 11. 定義 path

<path id="classpath">
  <fileset dir="./lib">
  <include name="**/*.jar"/>
  </fileset>
  </path>

如清單 11 所示,path 包括當前路徑下 lib 子目錄下的所有 jar 包文件。

7. 定義 prepare 任務

在運行這個工具前,需要創建輸出結果目錄,為接下來的運行做准備。創建目錄的動作 必須在一個任務中完成,所以這裡定義 prepare 任務以達到此目的。

清單 12. 定義 prepare 任務

<target name="prepare">
  <mkdir dir="${beam.classes}"/>
  </target>

在 Ant 的元素中,標簽 target 專門用來定義新任務,定義的新任務 prepare 的內容 是創建屬性 beam.classes 的值所代表的目錄,即輸出結果目錄。

8. 定義任務

前面已經通過 taskdef 指定了任務名為 beam,接下來定義任務的具體內容。

清單 13. 定義任務

1    <target name="beam"  depends="prepare"
  2      description="runs all code through beam.">
  3      <beam srcdir="${code.dir}"
  4          source="1.4"
  5          destdir="${beam.classes}">
  6          <classpath refid="classpath"/>
  7          <option>-- beam::compiler=D:\Work\Beam\Java\my_config.tcl</option>
  8          <option>-- beam::root=./class/beam</option>
  9          <option>-- beam::data=./class/beam/beam_data</option>
  10         <option>-- beam::display_analyzed_files</option>
  11         <option>--beam::parser_file=./class/beam/BEAM- parseErrors</option>
  12         <option>--beam::complaint_file=./class/beam/BEAM -messages</option>
  13         <option>--beam::stats_file=./class/beam/BEAM- functions</option>
  14      </beam>
  15    </target>

讓我們更詳細地分析這段代碼。

第 1 行:標簽 target 定義 beam 任務,標簽 depends 說明 beam 任務依賴於 prepare 任務,即運行 beam 任務前必須先運行 prepare 任務。

第 2 行:定義對該 beam 任務的描述。

第 3 行:定義元素 beam,並指定所要分析的 Java 源代碼的路徑 srcdir,本文是 code.dir。

第 4 行: source 為這個工具支持的 javac 的編譯器參數,指定所要分析的 Java 源 代碼的版本,本文是 1.4。

第 5 行: destdir 指定這個工具輸出結果的位置,本文是 beam.classes。

第 6 行: classpath 指定 Java 源文件所引用的一些類庫的位置,refid 表明引用之 前定義的 path 元素的 id 值。

第 7 行 - 第 13 行 都是專門用來控制這個工具行為的特殊參數,定義特殊參數需要通 過定義 option 元素來實現,option 元素的起始標簽後不能再定義任何屬性,這個工具的 特殊參數通過在 option 的正文中定義。這個工具所有的參數都以 --beam開頭,使用這種 不太尋常的前綴是為了盡量不與別的編譯器參數相沖突。

第 7 行: --beam::compiler:指定之前利用 beam_configure 腳本生成 的 Java 編譯 器配置文件 my_config.tcl 的位置。

第 8 行: --beam::root:指定其輸出結果的根路徑。

第 9 行: --beam::data:指定其輸出結果中分析數據的路徑,如果該目錄不存在,這 個工具會在運行時創建該目錄。

第 10 行: --beam::display_analyzed_files:指定該參數,會在分析 Java 源文件的 同時打印出源文件的全路徑。

第 11 行: --beam::parser_file:這個工具自帶一個解析器 parser,運行結束後,會 將 parser 消息寫入這裡指定的文件。

第 12 行: --beam::complaint_file:這是使用這個工具時最重要的參數,工具運行結 束後,會將分析出的所有 ERROR,MISTAKE 和 WARNING 都寫入這裡指定的文件中,程序員 通過此輸出文件來分析代碼中的錯誤。

第 13 行: --beam::stats_file:一些統計數據會存放在這裡指定的文件中。

運行

進入 Java 源文件的根目錄下,即 build.xml 所在的目錄,通過命令行調用 Ant 運行 這個工具,如清單 14:

清單 14. 運行

C:\apache-ant-1.7.0\bin\ant beam

C:\apache-ant-1.7.0\bin\ant:Ant 批處理文件的絕對路徑。

beam:build.xml 中定義的任務名。

在命令行上輸入命令後,就會得到類似清單 15 所示的運行結果。

清單 15. 運行結果

Buildfile: build.xml

prepare:

beam:
   [beam] BEAM: Analyzing  `D:\Work\java\com\ibm\config\CliVersionHandler.java'
   [beam] BEAM: Analyzing  `D:\Work\java\com\ibm\config\CliHandler.java'
   [beam] BEAM: Analyzing  `D:\Work\java\com\ibm\config\EssMetaClassHandler.java'
   [beam] BEAM: Analyzing  `D:\Work\java\com\ibm\config\CliResourceID.java'
   [beam] BEAM: Analyzing  `D:\Work\java\com\ibm\config\CliIOPortHandler.java'
   [beam] BEAM: Analyzing  `D:\Work\java\com\ibm\config\CliRankHandler.java'

BUILD SUCCESSFUL
Total time: 1 minutes 11 seconds 

成功運行完後,即可以在其輸出結果路徑 beam.classes 中發現生成了 build.xml 中定 義的 BEAM-messages,它記錄著這個工具報出的所有代碼缺陷(ERROR,MISTAKE 和 WARNING),通過分析並相應修改這些錯誤,從而達到提高代碼質量的目的。

分析輸出結果

BEAM-messages 中的這個工具的輸出結果,一般如清單 16 所示:

清單 16. 輸出結果

BEAM_VERSION=3.4.0
BEAM_ROOT=D:\Work\class\beam
BEAM_DIRECTORY_WRITE_INNOCENTS=D:\Work\class\beam\beam_data\innocent1
BEAM_DIRECTORY_WRITE_ERRORS=D:\Work\class\beam\beam_data\errors1

-- ERROR2   /*operating on NULL*/  >>>ERROR2_foo_55479ec82b606
"Example.java", line 11: invalid operation involving NULL pointer
ONE POSSIBLE PATH LEADING TO THE ERROR:
"Example.java",line 8: the if-condition is true(used as evidence that  error is possible)
"Example.java", line 11: using operation `[]' to dereference NULL  pointer `p'

VALUES AT THE END OF THE PATH:
p = 0

輸出結果中首先列出了這個工具的版本和輸出結果根目錄等相關配置信息,“--”之後 是缺陷類型名,缺陷類型有 ERROR,MISTAKE 和 WARNING,嚴重程度依次遞減。緊接著是代 碼出錯的行數和解釋,並附有詳細的出錯路徑和在這個出錯路徑結束時變量最終的取值。

BEAM 報出假錯誤的概率很低,但是也會有偶爾出錯的時候,如果發現這個工具報的缺陷 是“無辜”的,則可以將缺陷類型名後的注釋粘貼在源代碼中其所報錯的那一行後(如將清 單 16 中 ERROR2 後的注釋 /*operating on NULL*/ 粘貼在報錯的第 11 行後),以後再 運行時將不會再對此行代碼的這個錯誤進行報錯。

結束語

本文介紹了如何在 Windows 平台上運行靜態分析工具 BEAM 來檢查 Java 代碼的缺陷, 讀者通過本文介紹的詳細步驟,可以自行在 Windows 上運行 BEAM ,找出代碼隱患,達到 提高 Java 代碼質量的目的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved