Clang Static Analyzer (3) Cppcheck 原创 精华
Clang Static Analyzer (3) Cppcheck
前文介绍CodeChecker时,使用到了Cppcheck,我们来看看这个工具是什么,如何使用。
1、Cppcheck介绍
Cppcheck 是 C/C++ 代码的静态分析工具。它提供独特的代码分析技术来检测缺陷,不检查代码中的语法错误,只检查编译器检查不出来的缺陷,并专注于检测未定义行为错误和危险的编码结构。其目标是减少误报、零误报,检查代码中真正的错误。Cppcheck旨在能够分析C / C++代码,即使它具有非标准语法(在嵌入式项目中很常见)。
Cppcheck既有开源版本,也有具有扩展的功能和支持的Cppcheck Premium版本,。可以访问 www.cppchecksolutions.com 以获取商业版本的更多信息和购买选项。
1.1、Cppcheck功能特性
-
独特的代码分析,可检测代码中的各种错误。
-
命令行界面和图形用户界面都可用。
-
Cppcheck非常注重检测未定义的行为。
1.2、Cppcheck特有的分析技术
使用多个静态分析工具可能是一个好主意,每个工具都有独特的功能特性。这在研究中已经证实。那么Cppcheck的独特之处在哪里?
Cppcheck使用不健全的流程敏感分析,其他几种分析器使用基于抽象解释的路径敏感分析,这也很好,但既有优点也有缺点。从理论上讲,根据定义,路径敏感分析比流量敏感分析更好。但实际上,这意味着Cppcheck将检测其他工具无法检测到的错误。在Cppcheck中,数据流分析不仅是“前向”的,而且是“双向的”。大多数分析器会诊断这一点,可以确定数组索引为 1000 时会出现溢出。
void foo(int x)
{
int buf[10];
if (x == 1000)
buf[x] = 0; // <- ERROR
}
Cppcheck还将诊断此问题,当x等于1000时,赋值时也会出现数组越界。
void foo(int x)
{
int buf[10];
buf[x] = 0; // <- ERROR
if (x == 1000) {}
}
1.3、未定义行为Undefined behaviour
- Dead pointers 死指针
- Division by zero 除以零
- Integer overflows整数溢出
- Invalid bit shift operands无效的位移操作数
- Invalid conversions无效转化
- Invalid usage of STLSTL 的用法无效
- Memory management内存管理
- Null pointer dereferences空指针解引用
- Out of bounds checking越界检查
- Uninitialized variables未初始化的变量
- Writing const data写入常量数据
2、Cppcheck安装
Cppcheck也可以从各种包管理器安装;但是,您可能会得到一个过时的版本。为了获取更新版本,可以访问https://github.com/danmar/cppcheck进行源码安装。
- Debian:
sudo apt-get install cppcheck
- Fedora:
sudo yum install cppcheck
- macOS:
brew install cppcheck
3、使用入门
第一个测试程序,这里有一段简单的代码,我们命名为file1.c。
int main()
{
char a[10];
a[10] = 0;
return 0;
}
执行cppcheck file1.c
,输出如下:
zhushangyuan@DESKTOP-RPE9R4O:~/CSA$ cppcheck file1.c
Checking file1.c ...
[file1.c:4]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.
我们再试试上面说的例子,保存到file2.c。
void foo(int x)
{
int buf[10];
buf[x] = 0; // <- ERROR 1
if (x == 1000) {
buf[x] = 0; // <- ERROR 2
}
}
执行cppcheck --enable=all file2.c
,输出如下。可以看得出有2个warning和3个style问题。
zhushangyuan@DESKTOP-RPE9R4O:~/CSA$ cppcheck --enable=all file2.c
Checking file2.c ...
file2.c:4:6: warning: Either the condition 'x==1000' is redundant or the array 'buf[10]' is accessed at index 1000, which is out of bounds. [arrayIndexOutOfBoundsCond]
buf[x] = 0; // <- ERROR 1
^
file2.c:5:9: note: Assuming that condition 'x==1000' is not redundant
if (x == 1000) {
^
file2.c:4:6: note: Array index out of bounds
buf[x] = 0; // <- ERROR 1
^
file2.c:6:8: warning: Either the condition 'x==1000' is redundant or the array 'buf[10]' is accessed at index 1000, which is out of bounds. [arrayIndexOutOfBoundsCond]
buf[x] = 0; // <- ERROR 2
^
file2.c:5:9: note: Assuming that condition 'x==1000' is not redundant
if (x == 1000) {
^
file2.c:6:8: note: Array index out of bounds
buf[x] = 0; // <- ERROR 2
^
file2.c:4:10: style: Variable 'buf[x]' is assigned a value that is never used. [unreadVariable]
buf[x] = 0; // <- ERROR 1
^
file2.c:6:12: style: Variable 'buf[x]' is assigned a value that is never used. [unreadVariable]
buf[x] = 0; // <- ERROR 2
^
file2.c:1:0: style: The function 'foo' is never used. [unusedFunction]
^
3.1 检查文件夹
Cppcheck支持检查文件夹中的所有文件。通常一个项目会有许多源文件,如果需要同时检查,Cppcheck 可以检查文件夹中的所有文件.如果 path 是一个文件夹,cppcheck 将递归检查这个文件夹中的所有源文件。
cppcheck path
示例输出如下:
zhushangyuan@DESKTOP-RPE9R4O:~/CSA$ cppcheck .
Checking file1.c ...
file1.c:4:4: error: Array 'a[10]' accessed at index 10, which is out of bounds. [arrayIndexOutOfBounds]
a[10] = 0;
^
1/4 files checked 12% done
Checking file2.c ...
2/4 files checked 38% done
Checking hello.c ...
hello.c:2:13: error: Division by zero. [zerodiv]
int x = 7 / 0; // bug here
^
3/4 files checked 50% done
Checking simple.c ...
simple.c:16:11: error: Division by zero. [zerodiv]
return 5/(x-x); // warn
^
simple.c:12:5: error: Uninitialized variable: s [uninitvar]
f(s); // warn
^
simple.c:12:5: error: Uninitialized struct member: s.x [uninitStructMember]
f(s); // warn
^
4/4 files checked 100% done
3.2 手动检查文件或使用项目文件
使用 Cppcheck 可以手动检查文件,通过指定文件/文件夹来检查和设置,或者可以使用一个工程文件(cmake/visual studio)。
使用项目文件更快,因为它只需要非常少的配置。
手动检查文件可以更好的控制分析。
不一定哪种方法会有最好的结果,建议尝试一下,可能会得到不同的结果,发现大多数 bug 需要使用这两种方法。
4、严重级别Severities
输出信息中的严重级别支持如下几种:
- error 错误
when code is executed there is either undefined behavior or other error, such as
a memory leak or resource leak。发现未定义行为或其他错误,例如内存泄露、资源泄露 - warning告警
when code is executed there might be undefined behavior可能有未定义行为 - style样式风格
stylistic issues, such as unused functions, redundant code, constness, operator
precedence, possible mistakes.样式问题,例如未使用行数,冗余代码,常量性,操作符优先级,可能的错误等 - performance性能
run time performance suggestions based on common knowledge, though it is
not certain any measurable speed difference will be achieved by fixing these
messages.这些建议只是基于常识,即使修复这些消息,也不确定会得到任何可测量的性能提升。 - portability可移植性
portability warnings. Implementation defined behavior. 64-bit portability. Some
undefined behavior that probably works “as you want”, etc.可移植性警告。64 位的可移植性,代码可能在不同的编译器中运行结果不同。 - information信息
configuration problems, which does not relate to the syntactical correctness, but
the used Cppcheck configuration could be improved.配置问题,建议在配置期间仅启用这些
4.1 启用消息
默认情况下,只显示错误消息,可以通过 --enable 命令启用更多检查。
- 启用警告消息:
cppcheck --enable=warning file.c
- 启用性能消息:
cppcheck --enable=performance file.c
- 启用信息消息:
cppcheck --enable=information file.c
由于历史原因 --enable=style 可以启用警告、性能、可移植性和样式信息。当使用旧 XML 格式时,这些都由 style 表示:
cppcheck --enable=style file.c
- 启用警告和性能消息:
cppcheck --enable=warning,performance file.c
- 启用 unusedFunction 检查。这不能通过 --enable=style 启用,因为不会在库中正常工作。
cppcheck --enable=unusedFunction file.c
- 启用所有消息:
cppcheck --enable=all
5、常见错误修改
- 隐式构造问题
示例: (style) Class ‘Slice’ has a constructor with 1 argument that is not explicit.
解决方法:在Slice构造函数前加上explicit,使其必须显示构造,当然这种有时并非必须显示构造
- 变量未初始化问题
示例:(warning) Member variable ‘TableFileCreationInfo::file_size’ is not initialized in the constructor.
解决方法:在构造函数中加入变量初始值
- 变量/函数未使用问题
示例:(style) Unused variable: output
示例:(style) The function ‘rocksmt_wal_iter_status’ is never used.
解决方法:考虑后期是否还需要,不需要的及时删除,需要的保留
- raw loop问题
示例:(style) Consider using std::fill algorithm instead of a raw loop. [useStlAlgorithm]
示例:(style) Consider using std::transform algorithm instead of a raw loop. [useStlAlgorithm]
解决办法:将循环便利替换为STL标准库算法函数
- 引用传递问题
示例:(performance) Function parameter ‘f’ should be passed by reference.
解决办法:在声明function时,能用引用传递的尽量使用引用传递,尽量避免值传递
- const参数问题
示例:(performance) Function parameter ‘s’ should be passed by const reference. [passedByValue]
解决办法:形参s前加上const,在函数中未被修改的变量,尽量声明为const
跟着楼主学完整个系列,成为解决代码疑难杂症好手!
光是"只检查编译器检查不出来的缺陷"这个功能就很让人想去使用了
这个工具JAVA可以使用吗
不可以 文中提到的都是面向C/C++,Objective-C的。针对Java应该有类似的工具,例如,https://snyk.io/code-checker/java/。 还没有深入接触