代码静态分析
代码静态分析是指在不运行代码的情况下根据代码的静态信息,对代码的各个维度进行分析。
代码静态分析一般包括如下三个方面:
- 编码规范检查:保证代码的风格和团队的要求一致,对于多人合作的软件项目,这是代码易于理解和维护的基本要求。
- 代码静态度量:一般指代码复杂度度量,包括分支复杂度、圈复杂度、代码深度等。除此之外还包括代码重复率等指标。
- 代码缺陷检查:指通过静态分析发掘代码潜在的故障和缺陷。例如空指针异常、内存越界、堆栈溢出等潜在的软件bug。由于只是基于静态代码分析,所以故障缺陷只是意味存在着一个潜在的故障可能,仍需要人工事后分析。
代码静态分析可以通过人工分析和工具分析两种途径。人工分析也就是我们熟知的代码走查,被认为是一项提高软件质量非常有效的实践活动。然而对于拥有巨大代码库的软件,人工检查成本大,而且枯燥重复,这时借助工具可以大大提高代码静态检查的效率。让工具覆盖常见低级缺陷,进行大规模扫描,而让人去关注逻辑性强的分析检查,可以有效发挥工具和人的长处。另外工具也让自动化变为可能,可以有效提高工程效率。
代码静态分析的价值
对于软件,故障发现的时间越晚修复的成本会成指数上升趋势。有效的代码静态分析能够在软件运行之前暴露潜在的软件问题,对于大的软件项目可以节省巨大的成本。通过代码静态检查,可以获得如下收益:
- 可以提早发现代码的风格问题,第一时间对代码风格进行统一;
- 可以尽早的得到代码的度量数据,对代码的复杂度提供可靠数据,以指导重构工作的开展;
- 发现潜在的代码故障,避免故障泄露到后续的运行期,节省故障的定位成本和解决成本;
最后,将代码静态分析工具部署在持续集成服务器上,可以实时获得上述反馈。通过持续集成的实时监控和反馈,可以持续地防范代码的腐化和缺陷。
代码静态分析的途径
代码走查
一说起代码静态分析,人们往往先想到的是静态检查工具。事实上根据业界的统计,最有效的代码静态分析反而是人工代码走查。但是传统的人工走查由于缺少工具和流程的支撑,容易流为形式,因此Google和Facebook等企业都研发了简单易用的代码走查工具,并将代码走查活动部署在代码提交的关键路径上,使其成为代码提交流程的一部分。借助工具让代码走查变得不再麻烦,并通过将这一实践固化在代码提交流程中而避免了实践遗漏。
例如当前流行的Code Review工具:Facebook开源的Phabricator。Phabricator需要一台服务器进行部署,并配置和代码仓库(例如SVN)进行连接。开发人员代码提交会自动触发Phabricator发起一次代码评审活动,只有根据评审意见修改了代码被认定评审通过,代码才会被准许提交进SVN中。这一切都由Phabricator进行流程控制。另外Phabricator提供了一个交互式网页服务,在上面可以针对代码逐行评审并提交评审意见,被评审者可以对评审意见作出回复。可见Phabricator还提供了一个评审意见交流平台。
另外一个类似的工具是Gerrit,主要针对git仓库。
工具检查
通过工具对代码进行静态分析检查是一种更加高效的手段。静态分析工具一般分为静态度量工具和静态检查工具。
代码静态度量工具
静态度量工具主要是对代码进行静态复杂度度量。基本的包括代码行数,函数数等,再下来就是分支复杂度,圈复杂度,代码深度等等,通过这些数据,可以展现当前的代码规模以及复杂度情况。复杂度结果为代码提供了可视化数据,可以对代码的重构提供指导,还可以借助复杂度度量结果对代码的重构效果进行度量。另外将复杂度检查工具集成进持续集成服务器的话,可以设置规则避免过高复杂度的代码入库。新提交的代码只有在满足复杂度要求的情况下才允许合入代码库,这可以在第一时间防止代码腐化。目前大多数语言都可以找到开源易用的复杂度度量工具,这些工具有些可以直接集成进开发IDE工具中,让开发人员在写代码的时候就能获得度量结果。
代码静态检查工具
静态检查工具主要是通过扫描发掘代码中隐藏的潜在故障。一般通过以下维度衡量一个代码静态检查工具:
- 检查规则数:支持的检查规则越多则能发现越多的潜在故障;
- 误报率:指在报告的潜在故障中,非真实故障所在的比率。误报率越低越好;
- 易用性:有的工具支持本地部署检查,有的则需要把所有的检查数据上传服务器。不同工具的易用性差别挺大;
- 收费模式:免费还是收费,收费的话收费模式如何;
遗憾的是对于C/C++,当前市面上功能强大的代码静态检查工具皆是商用的。开源的工具在规则数以及对潜在故障的挖掘深度上和商用工具的差距还是挺明显的。一般由于商用工具比较昂贵,所以大多企业会选择将开源和收费工具结合使用,只在关键项目阶段部署使用商用工具,而开发人员日常扫描则使用开源工具。当然具体策略需要和产品类型项目情况等相结合。
代码静态分析工具对比
对于C++语言,代码度量工具大多选择SourceMonitor,这款软件是免费的,功能包括基本的代码行、函数数量、类数量统计,还包括分支比例度量、圈复杂度度量、代码深度度量等,并且会根据度量结果生成报表。
C++的代码静态检查工具,常用的有cppcheck、pclint、coverity等,这些软件除了可以检查代码规范,还可以扫描分析代码的潜在故障。我们按照以下维度进行对比:
对比项 | cppcheck | pclint | coverity | klocwork |
---|---|---|---|---|
收费模式 | 免费 | PC license $389 | 多模式收费;和代码规模和license类型有关。网站没有明确报价,具体报价需要咨询客服 | 收费;网站没有明确报价,需要根据软件规模和license类型具体咨询客服 |
支持语言 | C/C++ | C/C++ | C/C++/Java/C# | C/C++/Java/C# |
支持平台 | 跨平台 | 跨平台 | 跨平台 | 跨平台 |
支持Jenkins集成 | 支持 | 支持 | 支持 | 通过第三方插件 |
扫描结果 | 本地报表 | 本地结果 | 需上传服务器 | 需上传服务器 |
规则数 | 200+ | 900+ | 500+ | 没有官方数字 |
误报率 | 中等 | 高 | 低 | 低 |
易用性 | 易用 | 易用 | 复杂 | 复杂 |
从前面的分析可见,虽然开源的工具易用性高、容易和持续集成服务器进行集成,但缺点是扫描效果无论是在深度和广度上都不如商业工具。另外规则数越多误报率就越高,所以这是一个需要平衡的点。
代码静态分析工具部署
对于代码静态度量工具,由于是开源免费的,一般开发人员和持续集成服务器上都进行部署。开发人员代码提交前先自检,自检复杂度在要求范围内再提交入库。而持续集成服务器通过统一构建再进行确认。
而对于代码静态检查工具,从前面的分析可见,虽然开源的工具易用性高、容易和持续集成服务器进行集成,但缺点是扫描效果无论是在深度和广度上都不如商业工具。但这并不能说开源工具就没有了价值。一般项目的做法是在软件流水线的不同阶段部署不同的工具,来达到整体成本收益比最好。
一种常见的部署方式是,在每个开发人员的机器上部署cppcheck开源工具,这样每个开发人员都可以在本地实时对代码进行检查,将初级的错误直接消灭在本地。而持续集成上则部署商业静态检查工具,这样可以降低商业工具部署的机器数,还能降低商业工具的扫描次数,从而节省费用开支。一般在持续集成上可以设置每日或者每个发布版本用商业工具进行一次扫描,具体配置规则和项目特点有关。
集成了代码走查工具,代码度量工具和代码检查工具的持续集成服务器如下图。
其它注意事项
- 在代码静态检查之前,最好先将编译告警也消除掉。从检查基础的修复开始做起。
- 对检查工具报的错误要第一时间修复,不要累计。大规模修改引入的风险往往不可控。
- 由于工具会有误报,所以使用工具仍避免不了人工分析,人工分析的质量仍需要保证。
- 不要因为有了工具而忽略了人工代码走查的重要性。
- 由于静态分析只是依据代码的静态视图做分析,不能替代代码的运行态测试,例如单元测试。