0.背景
靜态代碼分析可以提高代碼品質和盡早的發現bugs,減少後期排查問題的時間。
Infer是facebook開源的一款代碼靜态分析工具,現支援的語言有Java、Objective-C、C和C++; 對Android和Java代碼可以發現null pointer exceptions和resource leaks等;對iOS、C和C++代碼可以發現memory leak等。
誰在使用,facebook、instagram、UBER、WhatsApp等等;
在facebook内部,由2個小團隊建構了這個靜态分析工具,支援了上千名工程師和百萬行級代碼。
本文将介紹Infer的使用。
1. 安裝Infer
infer隻支援Mac和Linux系統
1-1. docker方式
[root@localhost infer_docker]# curl -sSO https://raw.githubusercontent.com/facebook/infer/master/docker/Dockerfile
[root@localhost infer_docker]# curl -sSO https://raw.githubusercontent.com/facebook/infer/master/docker/run.sh
sh run.sh
1-2. Mac
hugangdeMacBook-Pro:~ hugang$ brew update
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- mach (LoadError)
解決辦法(重裝brew):
hugangdeMacBook-Pro:~ hugang# ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall)"
hugangdeMacBook-Pro:~ hugang# ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
hugangdeMacBook-Pro:~ hugang$ brew install infer
1-3. Linux下release版本
infer的依賴:https://github.com/facebook/infer/blob/master/INSTALL.md#pre-compiled-versions
Infer dependencies for Linux
Here are the prerequisites to be able to compile Infer on Linux. This is required to be able to use the release (faster), or to compile everything from source (see the end of this document).
opam >=
Python
Java (only needed for the Java analysis)
gcc >= or clang >= (only needed for the C/Objective-C analysis)
autoconf >= and automake >= (if building from git)
其中依賴opam(OPAM is a source-based package manager for OCaml)安裝如下:
http://opam.ocaml.org/doc/Install.html#FedoraCentOSandRHEL
http://software.opensuse.org/download.html?project=home%3Aocaml&package=opam
對于 CentOS ,請以 根使用者 root 運作下面指令:
cd /etc/yum.repos.d/
wget http://download.opensuse.org/repositories/home:ocaml/CentOS_7/home:ocaml.repo
yum install opam
對于 CentOS ,請以 根使用者 root 運作下面指令:
cd /etc/yum.repos.d/
wget http://download.opensuse.org/repositories/home:ocaml/CentOS_6/home:ocaml.repo
yum install opam
依賴解決後,下載下傳infer的release版本:
https://github.com/facebook/infer/releases/tag/v0.9.4.1
wget https://github.com/facebook/infer/releases/download/v0.9.4.1/infer-linux64-v0.9.4.1.tar.xz
tar xf infer-linux64-v0.tar.xz
cd infer-linux64-v0
./build-infer.sh
vim /etc/profile添加infer指令的路徑
export PATH=${infer安裝路徑}/infer/bin:$PATH
source /etc/profile
2.Infer工作流
Infer生成的所有檔案預設儲存在你執行infer指令路徑下的infer-out目錄中,可以用-o參數自定義輸出目錄。
Inger工作流包括2個動作:capture和analysis
capture:infer通過編譯程序将對應的C、C++、JAVA和OBJ-C代碼轉換成Infer中間語言(OCaml)。
analysis:對infer-out/captured的中間資料進行分析後的結果。
2-1. 全局工作流
預設情況下,每次運作infer會删除之前infer-out中的資料,即重新分析整個工程。
2-1-1. 單個檔案
2-1-2. 工程
使用
infer -- <your build command>
文法格式
比如你的項目是maven的,就執行
infer -- mvn compile
infer支援的build系統有:
Gradle、Buck、Maven、Xcodebuild、Make
2-2. 差異化工作流
通過Infer的reactive模式隻分析改動的代碼。
1. mvn clean
2. infer -a capture -- mvn compile
3. ### 改動代碼操作1
4. infer --reactive -- mvn compile
5. ### 改動代碼操作2
6. infer --reactive --continue -- mvn compile
第4行分析代碼操作1,第6行分析代碼操作1和2;
–reactive隻分析上一次改動;
–reactive –continue分析累積的改動;
2-3. 互動式檢視報告:inferTraceBugs
根據提示選擇某個具體檢測到的問題,列印出該問題對應的代碼。
3. 執行個體操作
下載下傳一個maven工程:
Infer對該工程進行靜态代碼分析
3-1. 全局工作流:
[[email protected] coveralls-maven-plugin]# infer -- mvn compile
Capturing in maven mode...
Translating source files ( classes)
Starting analysis...
legend:
"F" analyzing a file
"." analyzing a procedure
Found source files in /data0/hugang/coveralls-maven-plugin/infer-out
FFFFFFFFFFFF................................F..................................F....F...........F.F.......F....F...F.FF.........F...F.F..F....F.............FF......................F...F.......F.F..........F................FF..F................................FFF........FFF.............F....F..F.......F....F......FFF.......FF.F........................................................
Found issue
src/main/java/org/eluder/coveralls/maven/plugin/domain/GitRepository.java:: error: RESOURCE_LEAK
resource of type org.eclipse.jgit.revwalk.RevWalk acquired by call to new() at line is not released after line
private Git.Head getHead(final Repository repository) throws IOException {
ObjectId revision = repository.resolve(Constants.HEAD);
> RevCommit commit = new RevWalk(repository).parseCommit(revision);
Git.Head head = new Git.Head(
Summary of the reports
RESOURCE_LEAK:
發現一個可能會導緻資源洩露的問題。
3-2. 差異化工作流:
每次分析前,需清空之前的class檔案,即執行
mvn clean
[root@YY14070655 coveralls-maven-plugin]# mvn clean
[root@YY14070655 coveralls-maven-plugin]# infer -a capture -- mvn compile
Capturing in maven mode...
Translating source files ( classes)
進行差異化分析,在該工程中建立一個NullPointException.java檔案
[[email protected] plugin]# vim NullPointException.java
public class NullPointException {
public static void main(String[] args) {
String str = null;
if(str.equals("excepion")) {
System.out.println("woo");
}
}
}
[[email protected] coveralls-maven-plugin]# infer --reactive -- mvn compile
Capturing in maven mode...
Translating source files ( classes)
Starting analysis...
legend:
"F" analyzing a file
"." analyzing a procedure
Found source files in /data0/hugang/coveralls-maven-plugin/infer-out
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF..FFFFFFFFFFFFFFFFFF
Found issue
src/main/java/org/eluder/coveralls/maven/plugin/NullPointException.java:: error: NULL_DEREFERENCE
object str last assigned on line could be null and is dereferenced at line
String str = null;
> if(str.equals("excepion")) {
System.out.println("woo");
Summary of the reports
NULL_DEREFERENCE:
隻分析出本次新增的NullPointException.java代碼中的空引用問題。
3-3. 互動式檢視問題:
[[email protected] coveralls-maven-plugin]# inferTraceBugs
src/main/java/org/eluder/coveralls/maven/plugin/NullPointException.java:: error: NULL_DEREFERENCE
object str last assigned on line could be null and is dereferenced at line
Auto-selecting the only report.
Choose maximum level of nested procedures calls (default=max):
src/main/java/org/eluder/coveralls/maven/plugin/NullPointException.java:: error: NULL_DEREFERENCE
object str last assigned on line could be null and is dereferenced at line
Showing all steps of the trace
src/main/java/org/eluder/coveralls/maven/plugin/NullPointException.java:: start of procedure main(...)
public class NullPointException {
> public static void main(String[] args) {
src/main/java/org/eluder/coveralls/maven/plugin/NullPointException.java::
public static void main(String[] args) {
> String str = null;
if(str.equals("excepion")) {
src/main/java/org/eluder/coveralls/maven/plugin/NullPointException.java::
String str = null;
> if(str.equals("excepion")) {
System.out.println("woo");
4.Infer其他内部工具
4-1.Eradicate
嚴格檢查null point exception, 檢查@Nullable注解:
- 方法中參數和傳回
- 成員聲明
4-2. Checkers
文法檢查。
4-3. Linters
IOS app文法檢查。
5.infer-out結構
[[email protected] infer-out]# tree -L 1
.
├── attributes
├── backend_stats
├── bugs.txt
├── captured
├── frontend_stats
├── multicore
├── proc_stats.json
├── report.csv
├── reporting_stats
├── report.json
├── sources
├── specs
└── toplevel.log
directories, files
- captured:每個代碼檔案轉換成中間語言的資訊
每個java檔案都對應有一個目錄,形如:
AbstractServiceSetup.javad892b52872fa881fb360f3837218
The files contain serialized OCaml data structures.
每個目錄都包含cfg和cg檔案,形如:
AbstractServiceSetup.java.aa00cf389422c87fbf8271b732c88233.cfg
AbstractServiceSetup.java.aa00cf389422c87fbf8271b732c88233.cg
The .cfg file contains a control flow graph for each function or method implemented in the file.
The file .cg contains the call graph of the functions defined or called from that file.
- specs: 每個方法的分析結果
- bugs.txt,report.csv,report.json不同格式的結果集
6.自定義檢驗model
Infer對常用的類庫中一些方法都有對應的校驗model,以Java為例:
如果需要對代碼中引用的第三方庫方法建立校驗model,可以通過在
infer/models/java/src
下建立方法所在類的包路徑和方法所在類名的java檔案。
6-1. 建立model檔案
比如需要對一個第三方lib:com.github.neven7中對Model類中方法getNumber()新增校驗model,原代碼如下:
package com.github.neven7
public class Model {
public int getNumber() {
return ;
}
}
則對應建立
infer/models/java/src/com/github/neven7/Model.java
檔案:
package com.github.neven7
import com.facebook.infer.models.InferBuiltins;
import com.facebook.infer.models.InferUndefined;
public class Model {
public int getNumber() {
// 建立一個不确定的int
int num = InferUndefined.int_undefined();
// 假設num為0
InferBuiltins.assume(num == );
return num;
}
}
建立的這個model告訴使用了第三方lib中對Model類中方法getNumber()隻傳回0。
com.facebook.infer.models.InferBuiltins和com.facebook.infer.models.InferUndefined源碼:
https://github.com/facebook/infer/blob/0.9.4/infer/models/java/builtins/com/facebook/infer/builtins/InferUndefined.java
https://github.com/facebook/infer/blob/0.9.4/infer/models/java/builtins/com/facebook/infer/builtins/InferBuiltins.java
6-2. 重新編譯Infer
bash make -C infer
如果源碼未對getNumber()建立model, Infer對下面Test.java進行靜态分析時會報null pointer exception;如果新增了上述的model,Infer不會報null pointer exception,因為model知道getNumber()隻傳回0。
import com.github.neven7
public class Test {
public String getState() {
int num = new Model().getNumber();
if( == num) {
return "success";
} else {
return null;
}
}
}