本文主要介绍了我在开发中引入代码静态扫描的实践
写在前面(个人现状)
与单元测试一样,在我从事的嵌入式行业,代码扫描的理念并深入人心,或者说在日常开发中其实已经使用了代码扫面工具但是不自知,首先,静态的代码扫描是发生在编译之前的,在日常使用IDE进行代码编辑时,有部分工作IDE其实已经在做了,例如:
代码提示,上下文,头文件,定义缺失提示
编码规则检查并提示,语法错误(当然有的语法错误是编译不通过的)
除了以上属于代码扫面的范围外,代码扫描还包括:
编译可以通过的错误,如数组(指针)溢出,指针指向异常地址,除数为0等错误
编码风格的检查,变量、函数等的命名规则
适用于特定规则的检查,例如某个规则规定不允许使用switch分支,只能用if分支,可以进行约束
编码的缺陷
先举几个例子,在日常手写代码经常遇到的
经典案例1:
123for (uint8_t i = 0; i < 256;i++) { ...}
这段代码没有任何问题,然是运行的结果就是永远出不来,成了死循环;如果不预先静态检查,依赖于debug和运行调试才能发现。
经典案例2:
123for (uint8_t i = 50; i >= 0;i--) { ...}
这段代码好像也没有任何问题,但其实也是死循环;
案例1和案例2都可以通过静态扫面检出,在编写代码的时候就能发现;案例2也可以通过规则约束,如果制定了for循环条件中只能正向递增的规则,就能约束了。那么问题来了,为什么要制定约束呢?
还不是因为,在开发中经常容易写错,但是又不能保证人人都会去静态检测一遍,或者检查了忘记更改。干脆直接约束,禁用此写法。
MISRA-C和CERT-C编码规范
在工业以及如今的汽车制造业,流行并且加快普及着这两种编码规范。MISRA-C规范侧重于从代码书写规则来避免可能出现的Bug,而CERT-C侧重于代码的安全。
本章,我不打算展开讲这两个规范,而是介绍如何使用开源工具Cppcheck来进行C代码的检查,以及加载MISRA和CERT的检查插件。
使用Cppcheck进行MISRA规则检查
开源版本的Cppckeck支持MISRA-2012大部分规则的检查。这里主要介绍Windows下命令方式使用Cppcheck进行静态检查;当然Cppcheck本身带一个图形化的GUI,由于笔者使用不多,这里就不介绍了。
安装Cppcheck
在sourceforge上下载Cppcheck,傻瓜安装到自己的电脑上。
链接如下: https://sourceforge.net/projects/cppcheck/files/
安装Python
如果你的电脑已经安装了Python并配好了环境变量可以不用再安装了。CppCheck中插件是基于Python来执行的,所以Python环境是必不可少的。
下载htmlreport脚本
这个脚本主要是用来讲扫描的结果导出成html报告方便我们审查。
链接如下:
https://github.com/danmar/cppcheck/blob/main/htmlreport/cppcheck-htmlreport
实际我们只是用它的Python脚本。我们可以将它放到Cppcheck的安装包目录下:
准备需要扫描的代码(工程)
完整的工程如下:
在示例中,我们以一个github上的开源代码为例,在导入项目中时,对该代码进行扫描。这里以笔者近期使用的SHA256哈希的C语言实现为例子。
准备一个命令行或者Shell环境
如果是在Windows下,是可以直接使用命令行的。但是为了项目开发的一致,我推荐使用ConEmu作为终端工具统一管理环境变量。
新建一个Console.bat脚本文件
123456789101112131415161718@echo offset SPCANPATH=%~dp0set astmp=%ASPATH%set ASDISK=%astmp:~1,2%%ASDISK%cd %SPCANPATH%REM base env PATHset PATH=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0set PATH=D:\OpenSourceSw\cppcheck;%PATH%set PATH=D:\Anaconda3;D:\Anaconda3\Scripts;%PATH%set ConEmu=E:\MyWorkLog\02_dev_tools\ConEmu\ConEmu64.exestart %ConEmu% -title cppcheck ^ -runlist -new_console:d:"%SPCANPATH%":t:misra-2012
上面这段脚本主要时导入Python环境,笔者使用Anaconda进行Python环境管理,所以导入了Anaconda的路径,另外导入Cppcheck的安装路径,并启动ConEmu新建一个新的控制台。
记住,这个脚本只是用来启动可以执行代码扫描的环境终端。
准备Cppcheck检查的规则文件
Cppcheck支持文件过滤,引用文件路径,使用的插件等相关配置,这些配置我们都用独立的文件实现:
代码源文件清单code_filelist.txt
不好含文件夹/文件配置(支持正则表达)config_excludes.txt
包含头文件路径配置 includes.txt
启动MISRA插件配置misra-2021.json以及规则说明misra-2012.txt
抑制配置,这个主要配需要屏蔽的错误suppressions.txt
尽管需要配置的文件很多,但是作为入门,我想config_excludes、supressions就啥都不用配了,保持有一个空文件即可
code_filelist.txt 代码文件主要填源码c文件:
1E:\MyWorkLog\MainLine2\Autosar\AutosarZero\i01_source_code\Bsw\crypto\sha256.c
includes.txt 头文件目录填用到的.h文件,像通用的,例如stdlib、stddef这些可以不用管,只需要关注用户的头文件:
123E:\MyWorkLog\MainLine2\Autosar\AutosarZero\i01_source_code\BSW\common_includesE:\MyWorkLog\MainLine2\Autosar\AutosarZero\i03_intergration_testE:\MyWorkLog\MainLine2\Autosar\AutosarZero\i01_source_code\BSW\crypto
misra-2012.json:
123456{ "script": "misra.py", "args": [ "--rule-texts=misra-2012.txt" ]}
由于misra-2012规则版权限制,misra-2012.txt文件需利用互联网资源找一找,这里我就我贴出来。
准备好一切后,新建temp临时文件夹和report文件夹,然后编写cppcheck脚本,CppcheckReport.bat
1234567891011121314151617181920212223242526272829303132333435363738394041@echo offsetlocal@REM REM 获取当前日期和时间set DATETIME=%date:~0,4%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%%time:~6,2%@REM REM 设置 SRC_CC_REPORT 目录路径set SRC_CC_REPORT="report"@REM REM 检查 SRC_CC_REPORT 目录是否存在,如果不存在则创建if not exist %SRC_CC_REPORT% ( mkdir %SRC_CC_REPORT%)@REM 在 SRC_CC_REPORT 目录下创建 REPORT_YYYYMMDD_HHMMSS 子目录@REM set REPORT_SUB_DIR=%SRC_CC_REPORT%\RPT_%DATETIME%set REPORT_SUB_DIR=%SRC_CC_REPORT%mkdir %REPORT_SUB_DIR%del /f /s /q %REPORT_SUB_DIR%\*.*@REM set CPPCHECK_PATH="C:\Program Files\Cppcheck\cppcheck.exe"@REM set ADDON_PATH="C:\opt\cppcheck\configurations\misra.json"set SRC_FILELIST="code_filelist.txt"set OUTPUT_XML="static_analysis_output.xml"set CPPCHECK_HTMLREPORT_PATH="D:\OpenSourceSw\cppcheck\htmlreport\cppcheck-htmlreport"set CPPCHECK_HTMLREPORT_TITLE="Demo"set CPPCHECK_INCLUDES="includes.txt"set CPPCHECK_SUPPRESSIONS_LIST="suppressions.txt"set CPPCHECK_CONFIG_EXCLUDES_LIST="config_excludes.txt"cppcheck --cppcheck-build-dir=temp --file-list=%SRC_FILELIST% --xml --config-excludes-file=%CPPCHECK_CONFIG_EXCLUDES_LIST% --suppressions-list=%CPPCHECK_SUPPRESSIONS_LIST% --platform=win64 -D__GNUC__ -DCPPCHECK --enable=all --includes-file=%CPPCHECK_INCLUDES% --addon=misra-2012.json 2>%OUTPUT_XML%python %CPPCHECK_HTMLREPORT_PATH% --file=%OUTPUT_XML% --title=%CPPCHECK_HTMLREPORT_TITLE% --report-dir=%REPORT_SUB_DIR% --source-dir=.@REM REM 删除xml文件del %OUTPUT_XML%endlocal
这个脚本所做的事情就是执行代码扫描,临时文件存放到temp,报告信息输出到report,记得替换掉相关文件路径,主要是CPPCHECK_HTMLREPORT_PATH和CPPCHECK_HTMLREPORT_TITLE
可以看到,相关配置文件都被加载进来了。
运行静态扫描
双击运行Console.bat,启动命令行,然后再命令行中执行CppcheckReport.bat
运行之后,从report中打开index.html文件会看到相关扫描结果出现了,会详细罗列不满足项,
甚至我们可以点进入查看具体代码信息:
写在后面
由于时间有限,文章更新没那么频繁,如果有相关疑问的,可以关注公号后回复“聪拌面技术交流”,推荐入群交流。
许可协议
本文由Zhu原创,转载请注明出处。
分享文章