iOS静态分析:Infer的使用

一、Infer简介

Facebook 的 Infer 是一个静态分析工具。Infer 可以分析 Objective-C, Java 或者 C 代码,报告潜在的问题。任何人都可以使用 Infer 检测应用,这可以将那些严重的 bug 扼杀在发布之前,同时防止应用崩溃和性能低下。Infer 可检查 Android 和 Java 代码中的 NullPointException 和 资源泄露。除了以上,Infer 还可发现 iOS 和 C 代码中的内存泄露。

Infer 已经成为 Facebook 开发流程的一个环节,包括 Facebook Android 和 iOS 主客户端,Facebook Messenger, Instagram 在内的,以及其他影响亿万用户的手机应用,每次代码变更,都要经过 Infer 的检测。

Infer简介.png

二、Infer安装

1、Homebrew 安装

brew install infer

如果安装太久,或者安装失败,也可以使用下面的安装包的方式进行安装

2、安装包安装

1)GitHub下载安装包:https://github.com/facebook/infer/releases 选择对应的版本,进行下载。

2)下载后进行解压,解压后会有一个 infer-osx-v1.0.0 目录,v1.0.0是对应的版本号。主执行目录是infer-osx-v1.0.0/lib/infer/infer/bin/

3)设置PATH变量。建议把 Infer 的执行目录加入到环境变量中,这样使用起来会简便一些。当然,你也可以用绝对路径。本文档后续默认执行路径已加入到环境变量中。

open ~/.bash_profile
export PATH="${PATH}:/Users/zjh48/Documents/infer-osx-v1.0.0/lib/infer/infer/bin"
source ~/.bash_profile

注意 export PATH="${PATH}:/Users/zjh48/Documents/infer-osx-v1.0.0/lib/infer/infer/bin" 这个需要写成自己的路径

4)执行 infer --version,如果输出对应的版本,则说明安装成功

三、Infer的使用

1、Infer基本用法

我们可以新建一个测试工程InferTest,并在 ViewController.m 中,写上一段代码:

#import "ViewController.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString *str = @"1";
}

@end

首先用cd命令进入InferTest目录,然后运行以下命令进行编译:

infer -- xcodebuild -target InferTest -configuration Debug -sdk iphonesimulator

执行结束后,终端上面会展示错误信息,在项目所在目录下也会多出buildinfer-out文件夹,其中report.txt中的信息和终端上的信息一样

Infer编译分析出问题.png

可以看出,我们的 ViewController.m 代码里有1个问题,将代码修改如下:

NSString *str = @"1";
NSLog(@"str = %@",str);

先手动删除build文件夹(清除编译信息), 再次执行上述命令,重新编译:

infer -- xcodebuild -target InferTest -configuration Debug -sdk iphonesimulator

执行结果:

Infer编译分析没有问题.png

恭喜,问题已经消除,以上就是infer的基本用法。

2、可能出现的问题

1)出现xcode-select: error

xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance

这种情况是这种情况是xcodebuild的路径不正确。将路径切换到Xcode的目录下,如下:

sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer/

2)在两次执行编译命令的过程中,发现在没有对代码做任何更改的时候(bug还在),报出BUILD SUCCEEDED的提示:

Infer编译没变化.png

根据提示可以看到,此次build并没有分析任何文件。原因涉及到增量分析,默认编译的是有改变的代码,下一小节会详细介绍。

3、增量模式和非增量模式

第一次运行的时候,两种模式是一样的,都会对工程的所有文件进行编译检查,产生检查结果:
增量模式:当已经产生分析结果后(build和infer-out文件夹),再执行编译命令,即为增量模式。如有代码没有改动,则此次不会有编译结果产生,如果代码有新的改动,此次只产生新的编译结果。这种以增量为基准的原则叫做增量模式。
非增量模式:在删除了俩个文件夹的情况下,运行文件,会输出所有的编译信息,即此时处于非增量模式。

增量模式转化为非增量模式:

第1种、直接删除文件夹(build和infer-out文件夹)

第2种、先使用xcodebuild clean 清除一下对应的 target 工程,之后再重新编译分析

xcodebuild -target InferTest -configuration Debug -sdk iphonesimulator clean

4、分析带cocoapods的项目

1)执行命令

先识别 /***.xcworkspace,再识别 scheme

infer -- xcodebuild -workspace InferTest.xcworkspace -scheme InferTest -configuration Debug -sdk iphonesimulator

这种方法会分享所有的代码,而当我们在项目中使用了很多第三方的时候,其实我们只想让Infer分析我们的代码,而不想分析第三方的代码,不然分析报告中会有很多第三方的issue,看着混乱,这时我们可以用命令行过滤掉关于不想分析的文件。比如用到cocoapods的项目,我们想过滤掉Pods引入的第三方库。

2)过滤三方库

方法一

在命令行里添加过滤条件:INFER_ARGS="--skip-clang-analysis-in-path^[\"Pods\"]"

INFER_ARGS="--skip-clang-analysis-in-path^[\"Pods\"]" infer -- xcodebuild -workspace InferTest.xcworkspace -scheme InferTest -configuration Debug -sdk iphonesimulator

in-path 后面跟的是一个忽略文件或者文件夹的路径数组。

方法二

在工程目录下新建 .inferconfig 文件, 内容如图, 可以过滤掉Pods文件夹下的第三方库, skip-analysis-in-path是一个数组, 想要过滤其他文件, 只需要增加路径即可。

inferconfig配置文件.png

1、in-path 后面跟的是一个忽略文件或者文件夹的路径数组。
2、["Pods"] 代表过滤所有的 Pods 文件
3、["Pods/AFNetworking","Pods/Masonry"] 代表过滤 AFNetworking、Masonry 三方库
4、也可以写个脚本对解析出来的文件进行二次拆分,按组件模块、错误和警告划分

四、Infer工作流程

不管是分析哪种语言,Infer 运行时,分为两个主要阶段:捕获阶段、分析阶段

1、捕获阶段

Infer 捕获编译命令,将文件翻译成 Infer 内部的中间语言。

这种翻译和编译类似,Infer 从编译过程获取信息,并进行翻译。这就是我们调用 Infer 时带上一个编译命令的原因了,比如: infer -- clang -c file.c, infer -- javac File.java。结果就是文件照常编译,同时被 Infer 翻译成中间语言,留作第二阶段处理。特别注意的就是,如果没有文件被编译,那么也没有任何文件会被分析。

Infer 把中间文件存储在结果文件夹中,一般来说,这个文件夹会在运行 infer 的目录下创建,命名是 infer-out/。当然,你也可以通过 -o 选项来自定义文件夹名字

2、分析阶段

在分析阶段,Infer 分析 infer-out/ 下的所有文件。分析时,会单独分析每个方法和函数。

在分析一个函数的时候,如果发现错误,将会停止分析,但这不影响其他函数的继续分析。所以你在检查问题的时候,修复输出的错误之后,需要继续运行 Infer 进行检查,知道确认所有问题都已经修复。

错误除了会显示在标准输出之外,还会输出到文件 infer-out/bug.txt 中,我们过滤这些问题,仅显示最有可能存在的。在结果文件夹中(infer-out),同时还有一个 csv 文件 report.csv,这里包含了所有 Infer 产生的信息,包括:错误,警告和信息。

Infer 的工作流程图.png

五、常见扫描错误

1、DIRECT_ATOMIC_PROPERTY_ACCESS。

在代码中使用了使用了一个atomic的成员变量,infer建议我们将atomic修改为nonatomic。由于OC中,属性会被默认设置为atomic属性,我们需要显示将属性声明为nonatomic。关于atomic与nonatomic的区别可以参见文章https://my.oschina.net/linxiaoxi1993/blog/381332
该警告占了大概800个。在代码中主动设置成员变量的nonatomic属性,即可去除警告

2、ASSIGN_POINTER_WARNING

由于在mrc时代,没有weak指针,所以一些view的属性声明是、unsafe__unretain_的形式,在arc中,这个属性被判断为assign,需要将其修改为weak或者strong

3、NULL_DEREFERENCE

空指针的情况。根据具体代码的不同,出现空指针的情况也有所不同。
1)传参为0的情况下。例如代码中,在调用showAlertViewA()时,将tag传参为0,infer检测此处传0,判断为一个NULL空指针,所以爆出警告。这里可以理解为误报,不会出现问题。
2)通过malloc,calloc,realloc等函数申请内存,当内存不足时,有可能会在该函数中返回NULL,如果没有做NULL的判断,则警告
3)在创建NSArray或者NSDictionary时,传入的参数有可能会nil。由于NSArray与NSDictionary不接受空指针,所以在对其addObject或者setObject:forKey: 时需要进行判断一下是否为nil。

4、IVAR_NOT_NULL_CHECKED

在代码中调用block,运行代码时,没有做判空处理。即需要改动为,
if(block){block()}

5、BAD_POINTER_COMPARISON

Implicitly checking whether NSNumber pointer is nil。没有判断一个NSNumber类型的对象是不是空?此处应该是误报。

6、TAINTED_VALUE_REACHING_SENSITIVE_FUNCTION

代码中使用了cookie的value。可以理解为误报

7、PARAMETER_NOT_NULL_CHECKED

传参时没有判断是否为null,加一次判断就可以了

8、STRONG_DELEGATE_WARNING

将一个delegate属性设置为strong的类型。

9、PREMATURE_NIL_TERMINATION_ARGUMENT

没有判断是否为空

10、REGISTERED_OBSERVER_BEING_DEALLOCATED

创建一个对象后,监听了某些通知,但是没有在dealloc中释放该通知。项目中出现这种问题的类,基本都是单例,不会被销毁。

11、MEMORY_LEAK

内存泄露。项目代码全面启动了ARC进行内存管理,在OC层没有扫描出内存泄露。目前扫描出的内存泄露问题都是使用了malloc或者ralloc等c语言内存申请函数,在函数提前return前没有及时free。



参考链接:
infer官网:https://fbinfer.com/docs/getting-started.html
infer中文网站:https://infer.liaohuqiu.net
iOS infer静态分析工具使用:https://www.jianshu.com/p/fd1923cc87eb
iOS-App接入infer静态分析扫描工具:https://www.jianshu.com/p/52f61498e2b2
iOS开发之使用 infer静态代码扫描工具:https://www.cnblogs.com/ZachRobin/p/11280499.html
xcode-select --install 解决方案:https://blog.csdn.net/lucky9322/article/details/79036877

你可能感兴趣的:(iOS静态分析:Infer的使用)