软件测试|STATIC 代码静态验证工具 C/C++ 工具链设置指南
在使用 STATIC 代码静态验证工具(后文简略为'STATIC') 的过程中,客户最常遇到的困难之一就是工具链的设置问题。
工具链设置之所以困难,是因为不清楚为什么需要输入各种设定项,而且一旦出错,很难排查和解决。
为了帮助那些对工具链设置不熟悉的用户,本篇文章将为您简单易懂地整理出以下内容:
为什么需要进行工具链设置、STATIC是如何提取工具链信息的,以及一个简单的设置流程示例。
为什么需要进行工具链设置
要理解为什么需要进行工具链设置,首先需要了解C/C++代码是如何被转换为可执行文件的。
C/C++源代码会经过以下三个主要步骤,最终生成操作系统可以执行的程序:
预处理阶段(Preprocessing)
在进行C/C++开发时,为了减少重复代码的使用并提高可读性,通常会使用宏和头文件。
<图1> 预处理阶段
预处理阶段的作用就是展开定义的宏,并将分散的头文件整合成一个源代码文件,从而生成一个“翻译单元”(Translation Unit)。
编译阶段(Compiling)
在经过预处理之后,编译器会以预处理后的代码为基础,正式开始编译工作。
<图2> 编译阶段
这一过程可以大致分为以下四个阶段:
1. 词法分析(Lexical Analysis) 读取源代码并将其拆分为构成程序的最小单元(即“词法单元”,Token)。 例如,变量名、关键字、运算符等都会被识别为各自的标记。
2. 语法分析(Syntax Analysis) 对词法分析得到的标记进行语法检查,确认它们是否符合语言的语法规则。 在此阶段会生成“抽象语法树”(AST,Abstract Syntax Tree)。 AST是一种以树状结构层次化地表示程序结构的方式,是后续分析与优化的基础。
3. 语义分析(Semantic Analysis) 在生成的AST基础上,进一步理解程序的实际含义。 不仅检查语法是否正确,还会识别语义错误(Semantic Error),比如将整数与字符串相加、除零等不合逻辑或错误的运算。
4. 中间代码生成(Intermediate Code Generation) 在完成语法和语义分析之后,基于AST生成中间代码,作为转换为机器码前的中间步骤。 中间代码通常是与平台无关的形式,之后会用于最终代码的生成和优化过程。
链接阶段(Linking)
在完成编译阶段后,每个源文件会生成一个名为目标文件(Object File)的文件。但这些目标文件还不是最终的可执行文件,因为其中涉及到的外部函数或全局变量等信息的地址尚未确定,仍需进一步处理。
链接阶段(Linking)就是将这些目标文件与所需的库文件连接起来,生成操作系统可以执行的完整的可执行文件(Binary)的过程。链接过程主要包括以下三个步骤:
1. 符号解析与地址确定 识别各个目标文件中散布的函数和全局变量等“符号(Symbol)”,并为它们分配内存地址,使它们相互连接。这一过程的关键是找到在其他文件中定义的函数或变量,并将其正确连接起来。
2. 库文件合并 将程序中使用的标准库或用户自定义库与目标文件进行合并。对于需要外部引用的函数或类,从库中提取相应的实现并进行链接。像
printf()
、std::vector
这样的标准函数也是在这个阶段被链接进来的。3. 生成可执行文件 当所有符号都已正确链接、内存地址也全部确认后,就会生成一个操作系统能够识别和执行的二进制可执行文件。
<图3> 链接阶段
正如所见,从C/C++源代码生成最终可执行文件的过程中,会经历预处理、编译、链接等多个阶段,而每个阶段中编译器的设置和环境都会产生重要影响。
STATIC之所以需要了解这些构建过程,是因为它在进行静态分析时会模拟整个构建流程。因为,只有准确知道用户使用了哪种编译器和设置了哪些选项,才能进行精确的静态分析。
因此,工具链设置不仅仅是简单的参数输入,更是提升STATIC分析准确性和可信度的必要步骤。
STATIC如何提取工具链设置信息
为了让STATIC能够对源代码进行准确的分析,必须完整反映出用户实际使用的编译环境信息。 为此,STATIC使用了其自主开发的工具 CSBUILD,来自动提取并整理用户的工具链设置信息。
CSBUILD 主要执行以下三个核心任务:
<图4> 工具链设置提取流程
1. 收集分析所需的信息
CSBUILD会全面收集进行代码编译所需的各种信息,不仅包括源代码文件列表,还包括所使用的编译器类型与版本、编译时的选项、宏定义、系统头文件与用户自定义头文件路径等。
通过这些信息,STATIC可以在尽可能接近用户真实开发环境的条件下读取和分析代码。
2. 验证收集到的信息
CSBUILD会测试所收集的信息是否能够成功编译,以提前发现是否存在缺失的文件或路径配置错误等问题。
由于STATIC的分析是基于 AST(抽象语法树)进行的,一旦发生编译错误,将难以进行准确的分析。
因此,通过验证步骤可以最大限度地减少错误,并在必要时引导用户进行修复。
3. 上传至服务器
通过验证的信息会被转换成STATIC分析服务器能够识别的标准格式,并进行压缩上传。
服务器由此可以准确还原用户的实际开发环境,从而提供更加可靠和高质量的分析结果。
STATIC的目标不仅是分析源代码本身,更是要理解代码所处的开发环境和配置。 CSBUILD正是为了自动化这一过程而开发的工具,它有效降低了繁琐的工具链设置负担,让任何人都能更轻松地使用静态分析工具。
收集分析所需的信息
为考虑到用户所处的各种开发环境,STATIC提供了两种方式,帮助用户便捷地收集进行分析所需的信息:
1. SPEC 模式
SPEC 模式 是指用户手动编写 JSON 格式的文件,在其中输入分析所需的源代码列表、编译器信息、编译选项等。 虽然这种方式略显繁琐,但如果用户希望自由配置或精细调整构建环境,SPEC 模式就是一个非常有用的选择。
2. Build Hook 模式
Build Hook 模式 是在用户实际构建项目的过程中,自动收集分析所需信息的一种方式。 这一模式的关键技术就是Hooking。Hooking 是将开发者编写的 Hook 库插入到构建过程中,拦截 API 调用、消息传递或事件流程等信息的技术。
正如前文所述,STATIC的工作原理与编译器非常相似,因此在构建过程中通过 Hooking 获取的所有信息,几乎可以涵盖进行静态分析所需的全部配置。 借助 Hooking 技术,STATIC能够自然且全面地提取分析配置,无需用户额外操作,提升了分析的准确性与便捷性。
<图5> Hooking 流程
此外,用户还可以选择不同的方式来收集源代码中所使用的头文件。
·Built-in 方式: 该方式使用STATIC内置的预处理解析器(Parser)对源代码进行直接分析,从中提取出所需的头文件。 适用于不依赖实际构建过程、希望快速分析源代码的场景。
·Original 方式: 该方式是在用户实际的构建环境中执行构建任务,并在过程中原样提取出实际使用到的头文件。 这种方式可以最大限度地还原真实环境,确保分析结果与实际构建一致。
用户可根据项目特性和分析需求,灵活选择最合适的头文件收集方法。
验证
在前一阶段收集到信息的基础上,STATIC会在内部验证这些信息是否能够用于实际编译。
STATIC分析器并不真正进行编译,而是通过生成 AST(抽象语法树)来执行静态分析。 因此,如果代码无法正常编译,AST 也将无法正确生成,最终分析也会失败。
通过这个过程,可以验证所收集的信息是否准确,提前防止分析错误的发生。
上传服务器
CSBUILD 会将上述过程中收集到的源代码、编译器信息、编译选项以及分析配置等内容,按照与服务器约定好的格式进行保存,并进行压缩后上传至服务器。
用户的工具链设置流程
前面我们已经了解了为什么在 C/C++ 项目中需要进行复杂的工具链设置,以及STATIC是如何在内部收集这些信息的。
接下来,我们来看一下STATIC用户在实际使用过程中进行工具链设置的具体流程。
Build Hook 模式
<图6> Build Hook 模式画面
Build Hook 模式是在用户的实际环境中执行构建的同时,自动收集分析所需信息的一种方式。
首先,在STATIC设置界面的顶部,启用 Build Hooking 开关。
使用此模式时,需要输入构建命令和编译器的安装路径。
<图7> Configuration 过程
选择用于构建项目的编译器,并添加所使用的系统头文件路径。 如果有需要额外添加或排除的编译选项,可以分别填写在 "Configuration to add" 和 "Configuration to exclude" 中。
其中的 preprocessor 条目是用于选择之前提到的头文件收集方式,可选择 Original 或 Built-in 模式。
SPEC 模式
<图8> Build Hooking 开关关闭后添加文件的流程
SPEC 模式是用户手动输入要分析的文件及环境配置信息的方式。
首先,在STATIC设置界面顶部,将 Build Hooking 开关关闭。 然后,手动添加需要进行分析的源文件。
<图9> 编译信息,头文件路径输入流程
接下来,输入所使用的编译器信息和头文件路径。 在这个过程中,与 Build Hook 模式不同,无需单独选择头文件收集方式。
由于 SPEC 模式并不会在用户环境中实际执行构建,因此默认使用STATIC内置的解析器(Built-in Parser) 来收集头文件。 换句话说,分析环境完全依赖于用户所输入的信息来构建。
总结
STATIC通过静态分析,在bug 实际发生之前就提前发现潜在问题,帮助开发者节省大量时间与成本,并显著提升代码质量。
此外,STATIC还提供了多种编码风格规范检查规则,即使不是 bug,也能帮助找出可优化的代码,进一步改善代码结构。
尤其是在 C/C++ 这类开发环境多样且复杂的语言中,工具链配置常常令人望而却步。 STATIC致力于简化和自动化这一流程,让任何人都能轻松开展静态代码分析工作。