使用 Clang 进行静态代码检测
一、Clang Static Analyzer 简介
Clang Static Analyzer 是一个工业级的静态源码检测工具,可以用来发现 C、C++ 和 Objective-C 程序中的 Bug。它既可以作为一个独立工具(scan-build)使用,也可以集成在 Xcode 中使用。
Clang Static Analyzer 建立在 Clang 和 LLVM 之上。严格地讲,它是 Clang 的一部分,因此它是完全开源的。Clang Static Analyzer 使用的静态分析引擎被实现为一个 C++ 库,可以在不同的客户端中重用,因此拥有很高的可扩展性。
二、scan-build 的使用
scan-build 就是 Clang Static Analyzer 的命令行工具。在 Clang 的二进制包中就可以直接找到它的身影,一般它与 Clang/Clang++ 同处于一个目录。
我们从一个示例开始:
1 2 3 4 5 6 7 8 9 | #include<stdio.h> #include<stdlib.h> int main(int argc, const char* argv[]) { void* ptr = malloc(sizeof(char)); free(ptr); memset(ptr, 0, sizeof(char) * 2); return 0; } |
scan-build 是在编译过程中检测代码的,因此必须和编译器一起协同工作,可以指定使用 gcc 或者 clang。注意,scan-build 会使用 clang/clang++ 做 analyze,即便指定编译器为 gcc/g++。
1 | scan-build -o memleak gcc memleak.c |
上述命令即对 memleak.c 进行检测,其中 -o 参数用于指定检测结果存放路径,检测结果会以 html 文件的形式保存:
可看到上述代码中的内存泄露问题被检测出来。以上是对于单个源码文件的检测,更好的方式是将 scan-build 直接与构建系统串接起来一起协同工作:
1 | $ scan-build --use-cc gcc --use-c++ g++ make |
三、使用 scan-build 处理交叉编译
交叉编译的场景下,除了指定 toolchain 之外,还要指定 analyzer-target,另外还需要指定 C/C++ 的 include 路径:
1 2 3 4 5 6 7 | $ C_INCLUDE_PATH=path_1:path_2:...:path_n \ CPLUS_INCLUDE_PATH=path_1:path_2:...:path_n \ scan-build \ --use-cc=arm-linux-gnueabihf-gcc \ --use-c++=arm-linux-gnueabihf-g++ \ --analyzer-target=arm-linux-gnueabihf \ make [options] |
其中 C_INCLUDE_PATH 和 CPLUS_INCLUDE_PATH 这两个变量都支持指定多个路径,中间使用冒号分隔。
对于大型程序,scan-build 会显著拖慢编译速度。另外,这类静态代码检测工具也有它的局限性,像内存访问越界类似的问题就不太可能检测得到,这时就要靠 Valgrind 和 Address Sanitizer 这些运行时的内存调试工具了。
参考: