静态代码分析可以提高代码质量和尽早的发现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的使用。
infer只支持Mac和Linux系统
[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
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
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 >= 1.2.0
Python 2.7
Java (only needed for the Java analysis)
gcc >= 4.7.2 or clang >= 3.1 (only needed for the C/Objective-C analysis)
autoconf >= 2.63 and automake >= 1.11.1 (if building from git)
http://opam.ocaml.org/doc/Install.html#FedoraCentOSandRHEL
http://software.opensuse.org/download.html?project=home%3Aocaml&package=opam
对于 CentOS 7,请以 根用户 root 运行下面命令:
cd /etc/yum.repos.d/
wget http://download.opensuse.org/repositories/home:ocaml/CentOS_7/home:ocaml.repo
yum install opam
对于 CentOS 6,请以 根用户 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.9.4.1.tar.xz
cd infer-linux64-v0.9.4.1
./build-infer.sh
vim /etc/profile添加infer命令的路径
export PATH=${infer安装路径}/infer/bin:$PATH
source /etc/profile
Infer生成的所有文件默认保存在你执行infer命令路径下的infer-out目录中,可以用-o参数自定义输出目录。
Inger工作流包括2个动作:capture和analysis
capture:infer通过编译进程将对应的C、C++、JAVA和OBJ-C代码转换成Infer中间语言(OCaml)。
analysis:对infer-out/captured的中间数据进行分析后的结果。
默认情况下,每次运行infer会删除之前infer-out中的数据,即重新分析整个工程。
infer -- javac Hello.java
使用infer --
语法格式
比如你的项目是maven的,就执行infer -- mvn compile
infer支持的build系统有:
Gradle、Buck、Maven、Xcodebuild、Make
通过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分析累积的改动;
根据提示选择某个具体检测到的问题,打印出该问题对应的代码。
下载一个maven工程:
[root@YY14070655 hugang]# git clone https://github.com/trautonen/coveralls-maven-plugin.git
[root@YY14070655 hugang]# cd coveralls-maven-plugin/
Infer对该工程进行静态代码分析
[root@YY14070655 coveralls-maven-plugin]# infer -- mvn compile
Capturing in maven mode...
Translating 54 source files (62 classes)
Starting analysis...
legend:
"F" analyzing a file
"." analyzing a procedure
Found 54 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 1 issue
src/main/java/org/eluder/coveralls/maven/plugin/domain/GitRepository.java:61: error: RESOURCE_LEAK
resource of type org.eclipse.jgit.revwalk.RevWalk acquired by call to new() at line 61 is not released after line 61
59. private Git.Head getHead(final Repository repository) throws IOException {
60. ObjectId revision = repository.resolve(Constants.HEAD);
61. > RevCommit commit = new RevWalk(repository).parseCommit(revision);
62. Git.Head head = new Git.Head(
63.
Summary of the reports
RESOURCE_LEAK: 1
发现一个可能会导致资源泄露的问题。
每次分析前,需清空之前的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 54 source files (62 classes)
进行差异化分析,在该工程中新建一个NullPointException.java文件
[root@YY14070655 plugin]# vim NullPointException.java
public class NullPointException {
public static void main(String[] args) {
String str = null;
if(str.equals("excepion")) {
System.out.println("woo");
}
}
}
[root@YY14070655 coveralls-maven-plugin]# infer --reactive -- mvn compile
Capturing in maven mode...
Translating 54 source files (62 classes)
Starting analysis...
legend:
"F" analyzing a file
"." analyzing a procedure
Found 55 source files in /data0/hugang/coveralls-maven-plugin/infer-out
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF..FFFFFFFFFFFFFFFFFF
Found 1 issue
src/main/java/org/eluder/coveralls/maven/plugin/NullPointException.java:5: error: NULL_DEREFERENCE
object str last assigned on line 4 could be null and is dereferenced at line 5
3.
4. String str = null;
5. > if(str.equals("excepion")) {
6. System.out.println("woo");
7.
Summary of the reports
NULL_DEREFERENCE: 1
只分析出本次新增的NullPointException.java代码中的空引用问题。
[root@YY14070655 coveralls-maven-plugin]# inferTraceBugs
0. src/main/java/org/eluder/coveralls/maven/plugin/NullPointException.java:5: error: NULL_DEREFERENCE
object str last assigned on line 4 could be null and is dereferenced at line 5
Auto-selecting the only report.
Choose maximum level of nested procedures calls (default=max):
src/main/java/org/eluder/coveralls/maven/plugin/NullPointException.java:5: error: NULL_DEREFERENCE
object str last assigned on line 4 could be null and is dereferenced at line 5
Showing all 3 steps of the trace
src/main/java/org/eluder/coveralls/maven/plugin/NullPointException.java:2: start of procedure main(...)
1. public class NullPointException {
2. > public static void main(String[] args) {
3.
4.
src/main/java/org/eluder/coveralls/maven/plugin/NullPointException.java:4:
2. public static void main(String[] args) {
3.
4. > String str = null;
5. if(str.equals("excepion")) {
6.
src/main/java/org/eluder/coveralls/maven/plugin/NullPointException.java:5:
3.
4. String str = null;
5. > if(str.equals("excepion")) {
6. System.out.println("woo");
7.
严格检查null point exception, 检查@Nullable注解:
方法中参数和返回
成员声明
infer -a eradicate -- mvn compile
语法检查。
infer -a checkers -- mvn compile
IOS app语法检查。
infer -a linters -- clang -c Test.m
[root@YY14070655 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
8 directories, 5 files
每个java文件都对应有一个目录,形如:
AbstractServiceSetup.java.0811d892b52872fa881fb360f3837218
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.
Infer对常用的类库中一些方法都有对应的校验model,以Java为例:
如果需要对代码中引用的第三方库方法新建校验model,可以通过在infer/models/java/src
下新建方法所在类的包路径和方法所在类名的java文件。
比如需要对一个第三方lib:com.github.neven7中对Model类中方法getNumber()新增校验model,原代码如下:
package com.github.neven7
public class Model {
public int getNumber() {
return 0;
}
}
则对应新建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 == 0);
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
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(0 == num) {
return "success";
} else {
return null;
}
}
}