LLVM 之 Clang 源码分析篇(2):clang::ento::CheckerRegistryData 结构体
Author: stormQ
Created: Thursday, 15. April 2021 00:18AM
Last Modified: Saturday, 17. April 2021 09:27PM
本文基于release/12.x
版本的 LLVM 源码,研究了clang::ento::CheckerRegistryData
结构体中各数据成员表示的意义以及成员函数的作用。从而,有助于理解 Clang 静态分析器源码实现的其他相关部分。
结构体clang::ento::CheckerRegistryData
的源码实现目录如下:
头文件为clang/include/clang/StaticAnalyzer/Core/CheckerRegistryData.h
源文件为clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp
step 1: 数据成员Checkers
Checkers
是结构体CheckerRegistryData
的数据成员之一。其定义如下:
197 CheckerInfoList Checkers;
查看数据类型CheckerInfoList
的定义:
87 using CheckerInfoList = std::vector<CheckerInfo>;
因此,数据成员Checkers
是一个std::vector
向量,其元素的数据类型为CheckerInfo
。
通过搜索源码发现,类clang::ento::CheckerRegistry
中的数据成员Data
的数据类型为CheckerRegistryData&
。因此,可以通过研究clang::ento::CheckerRegistry::Data.Checkers
对象的初始化过程,从而理解该数据成员的意义。
1) 哪些函数会添加Data.Checkers
对象的元素
通过搜索源码发现, Data.Checkers
对象添加元素的操作仅发生在CheckerRegistry::addChecker()
函数中。
2) CheckerRegistry::addChecker()
函数的源码实现
该函数的源码实现如下(定义在 clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp 中):
435 void CheckerRegistry::addChecker(RegisterCheckerFn Rfn,
436 ShouldRegisterFunction Sfn, StringRef Name,
437 StringRef Desc, StringRef DocsUri,
438 bool IsHidden) {
439 Data.Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri, IsHidden);
// 省略 ...
448 }
从上面的代码可以看出,该函数直接在 Data.Checkers
对象中构造了数据类型为CheckerInfo
的元素。
通过 GDB 调试进入CheckerRegistry::addChecker()
函数后,打印其函数参数的值。打印结果之一如下:
(gdb) i args
this = 0x7fffffffbb80
Rfn = 0x7fffe31e7fef <clang::ento::registerAnalysisOrderChecker(clang::ento::CheckerManager&)>
Sfn = 0x7fffe31e800e <clang::ento::shouldRegisterAnalysisOrderChecker(clang::ento::CheckerManager const&)>
Name = {static npos = 18446744073709551615, Data = 0x7fffe9feb8f9 "debug.AnalysisOrder", Length = 19}
Desc = {static npos = 18446744073709551615, Data = 0x7fffe9feb8c0 "Print callbacks that are called during analysis in order", Length = 56}
DocsUri = {static npos = 18446744073709551615, Data = 0x7fffe9feb8be "", Length = 0}
IsHidden = true
从上面的结果可以看出:
参数Rfn
的值为clang::ento::registerAnalysisOrderChecker()
函数的运行时内存地址。
参数Sfn
的值为clang::ento::shouldRegisterAnalysisOrderChecker()
函数的运行时内存地址。
参数Name
的值为"debug.AnalysisOrder"
。
参数Desc
的值为"Print callbacks that are called during analysis in order"
。
参数DocsUri
的值为""
。
参数IsHidden
的值为true
。
那么,这些函数参数的值是从何而来的呢?
3) CheckerRegistry::addChecker()
函数被调用的地方有哪些
通过 GDB 查看该函数的调用堆栈如下(部分):
#0 0x00007fffe9e70e70 in clang::ento::CheckerRegistry::addChecker(void (*)(clang::ento::CheckerManager&), bool (*)(clang::ento::CheckerManager const&), llvm::StringRef, llvm::StringRef, llvm::StringRef, bool)@plt () from /usr/local/bin/../lib/../lib/libclangStaticAnalyzerFrontend.so.12
#1 0x00007fffe9f87ffd in clang::ento::CheckerRegistry::CheckerRegistry(clang::ento::CheckerRegistryData&, llvm::ArrayRef<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, clang::DiagnosticsEngine&, clang::AnalyzerOptions&, llvm::ArrayRef<std::function<void (clang::ento::CheckerRegistry&)> >) (
this=0x7fffffffbb90, Data=..., Plugins=..., Diags=..., AnOpts=..., CheckerRegistrationFns=...)
at tools/clang/include/clang/StaticAnalyzer/Checkers/Checkers.inc:63
#2 0x00007fffe9fa20e4 in clang::ento::CheckerManager::CheckerManager(clang::ASTContext&, clang::AnalyzerOptions&, clang::Preprocessor const&, llvm::ArrayRef<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, llvm::ArrayRef<std::function<void (clang::ento::CheckerRegistry&)> >) (
this=0x555555660c20, Context=..., AOptions=..., PP=..., plugins=..., checkerRegistrationFns=...)
at /home/xxq/git-projects/llvm-project/clang/lib/StaticAnalyzer/Frontend/CreateCheckerManager.cpp:28
省略 ...
从上面的调用堆栈信息可以看出,该函数的直接调用者为CheckerRegistry::CheckerRegistry()
构造函数。
4) CheckerRegistry::CheckerRegistry()
构造函数的源码实现
CheckerRegistry::CheckerRegistry()
构造函数中与调用CheckerRegistry::addChecker()
函数相关的代码如下:
51 CheckerRegistry::CheckerRegistry(
52 CheckerRegistryData &Data, ArrayRef<std::string> Plugins,
53 DiagnosticsEngine &Diags, AnalyzerOptions &AnOpts,
54 ArrayRef<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns)
55 : Data(Data), Diags(Diags), AnOpts(AnOpts) {
56
57 // Register builtin checkers.
58 #define GET_CHECKERS
59 #define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
60 addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT, \
61 DOC_URI, IS_HIDDEN);
62
// 省略 ...
65
66 #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
67 #undef CHECKER
68 #undef GET_CHECKERS
// 省略 ...
188 }
从上面的代码可以看出,如果在CheckerRegistry::CheckerRegistry()
构造函数中调用CHECKER
宏,那么实质上就是调用CheckerRegistry::addChecker()
函数。
而Checkers.inc
文件中包含了大量的CHECKER
宏调用。内容如下(部分):
// 省略 ...
61 #ifdef GET_CHECKERS
62
63 CHECKER("debug.AnalysisOrder", AnalysisOrderChecker, "Print callbacks that are called during analysis in order", "", true)
// 省略 ...
250 #endif // GET_CHECKERS
// 省略 ...
因此,函数CheckerRegistry::addChecker()
的参数值直接来自于Checkers.inc
文件。
而Checkers.inc
文件是由工具TableGen
根据Checkers.td
文件的内容在编译 Clang 时生成的。编译完成后,该文件位于构建目录的子文件夹tools/clang/include/clang/StaticAnalyzer/Checkers/
中。
因此,函数CheckerRegistry::addChecker()
的参数值是由Checkers.td
文件的内容决定的。
从上述分析可以得出, 结构体CheckerRegistryData
中的数据成员Checkers
的作用为:用于存储所有可以被注册的检查器的相关数据。这些检查器正是Checkers.td
文件中所描述的那些检查器。
step 2: 数据成员EnabledCheckers
EnabledCheckers
是结构体CheckerRegistryData
的数据成员之一。其定义如下:
195 CheckerInfoSet EnabledCheckers;
数据类型CheckerInfoSet
的定义如下(定义在 clang/include/clang/StaticAnalyzer/Core/CheckerRegistryData.h 文件中):
90 using CheckerInfoSet = llvm::SetVector<const CheckerInfo *>;
因此,数据成员EnabledCheckers
是一个llvm::SetVector
向量,其元素的数据类型为const CheckerInfo *
。
类似地,可以通过研究clang::ento::CheckerRegistry::Data.EnabledCheckers
对象的初始化过程,从而理解该数据成员的意义。
1) 哪些函数会添加Data.EnabledCheckers
对象的元素
通过搜索源码发现, Data.EnabledCheckers
对象添加元素的操作仅发生在CheckerRegistry::initializeRegistry()
函数中。
2) 分析CheckerRegistry::initializeRegistry()
函数
CheckerRegistry::initializeRegistry()
函数的作用为:收集所有启用的检查器及其所有依赖的检查器。依赖的检查器包括如下几种:
直接和间接强依赖的检查器
直接和间接弱依赖的检查器
弱依赖检查器所直接和间接强依赖的检查器
注:这里直接给出了结论,具体研究过程可参考笔者的另一篇文章:《LLVM 之 Clang 源码分析篇(3):clang::ento::CheckerRegistry 类》。
因此, 结构体CheckerRegistryData
中的数据成员EnabledCheckers
的作用为:用于存储所有启用的检查器及其所有依赖的检查器。
step 3: 数据成员Packages
待补充
step 4: 数据成员PackageSizes
待补充
step 5: 数据成员PackageOptions
待补充
step 6: 数据成员CheckerOptions
待补充
step 7: 数据成员Dependencies
Dependencies
是结构体CheckerRegistryData
的数据成员之一。其定义如下:
209 llvm::SmallVector<std::pair<StringRef, StringRef>, 0> Dependencies;
注:数据类型llvm::SmallVector<std::pair<StringRef, StringRef>, 0>
表示向量类型,其元素的数据类型为std::pair<StringRef, StringRef>
,初始元素个数为 0。
因此,数据成员Dependencies
是一个llvm::SmallVector
向量,其元素的数据类型为std::pair<StringRef, StringRef>
。
类似地,可以通过研究clang::ento::CheckerRegistry::Data.Dependencies
对象的初始化过程,从而理解该数据成员的意义。
1) 哪些函数会添加Data.Dependencies
对象的元素
通过搜索源码发现,Data.Dependencies
对象添加元素的操作仅发生在CheckerRegistry::addDependency()
函数中。
2) CheckerRegistry::addDependency()
函数的源码实现
该函数的源码实现如下(定义在 clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp 中):
327 void CheckerRegistry::addDependency(StringRef FullName, StringRef Dependency) {
328 Data.Dependencies.emplace_back(FullName, Dependency);
329 }
从上面的代码可以看出,该函数直接在 Data.Dependencies
对象中构造了数据类型为std::pair<StringRef, StringRef>
的元素。
通过搜索源码发现,仅在CheckerRegistry::CheckerRegistry()
构造函数中会调用CheckerRegistry::addDependency()
函数。
3) CheckerRegistry::CheckerRegistry()
构造函数的源码实现
CheckerRegistry::CheckerRegistry()
构造函数中与调用CheckerRegistry::addDependency()
函数相关的代码如下:
51 CheckerRegistry::CheckerRegistry(
52 CheckerRegistryData &Data, ArrayRef<std::string> Plugins,
53 DiagnosticsEngine &Diags, AnalyzerOptions &AnOpts,
54 ArrayRef<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns)
55 : Data(Data), Diags(Diags), AnOpts(AnOpts) {
// 省略 ...
118 #define GET_CHECKER_DEPENDENCIES
119
120 #define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) \
121 addDependency(FULLNAME, DEPENDENCY);
// 省略 ...
140 #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
141 #undef CHECKER_DEPENDENCY
142 #undef GET_CHECKER_DEPENDENCIES
// 省略 ...
188 }
从上面的代码可以看出,如果在CheckerRegistry::CheckerRegistry()
构造函数中调用CHECKER_DEPENDENCY
宏,那么实质上就是调用CheckerRegistry::addDependency()
函数。
而Checkers.inc
文件中包含了大量的CHECKER_DEPENDENCY
宏调用。内容如下(部分):
// 省略 ...
253 #ifdef GET_CHECKER_DEPENDENCIES
254 CHECKER_DEPENDENCY("alpha.core.C11Lock", "alpha.core.PthreadLockBase")
// 省略 ...
316 #endif // GET_CHECKER_DEPENDENCIES
// 省略 ...
因此,函数CheckerRegistry::addDependency()
的参数值直接来自于Checkers.inc
文件,并且是由Checkers.td
文件的内容决定的。
Checkers.inc:254 行的宏定义表示:检查器alpha.core.C11Lock
强依赖于检查器alpha.core.PthreadLockBase
。
因此,CHECKER_DEPENDENCY
宏定义的作用为:用于指定检查器之间的强依赖关系。该宏的两个参数都表示检查器的全称,并且前者直接强依赖于后者。
因此, 结构体CheckerRegistryData
中的数据成员Dependencies
的作用为:用于存储检查器之间的直接强依赖关系。其元素的first
字段表示一个检查器的全称,second
字段表示其直接强依赖的检查器的全称。
step 8: 数据成员WeakDependencies
WeakDependencies
是结构体CheckerRegistryData
的数据成员之一。其定义如下:
210 llvm::SmallVector<std::pair<StringRef, StringRef>, 0> WeakDependencies;
因此,数据成员WeakDependencies
也是一个llvm::SmallVector
向量,其元素的数据类型为std::pair<StringRef, StringRef>
。
类似地,可以通过研究clang::ento::CheckerRegistry::Data.WeakDependencies
对象的初始化过程,从而理解该数据成员的意义。
1) 哪些函数会添加Data.WeakDependencies
对象的元素
通过搜索源码发现,Data.WeakDependencies
对象添加元素的操作仅发生在CheckerRegistry::addWeakDependency()
函数。
2) CheckerRegistry::addWeakDependency()
函数的源码实现
该函数的源码实现如下(定义在 clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp 中):
331 void CheckerRegistry::addWeakDependency(StringRef FullName,
332 StringRef Dependency) {
333 Data.WeakDependencies.emplace_back(FullName, Dependency);
334 }
从上面的代码可以看出,该函数直接在 Data.WeakDependencies
对象中构造了数据类型为std::pair<StringRef, StringRef>
的元素。
通过搜索源码发现,仅在CheckerRegistry::CheckerRegistry()
构造函数中会调用CheckerRegistry::addWeakDependency()
函数。
3) CheckerRegistry::CheckerRegistry()
构造函数的源码实现
CheckerRegistry::CheckerRegistry()
构造函数中与调用CheckerRegistry::addWeakDependency()
函数相关的代码如下:
51 CheckerRegistry::CheckerRegistry(
52 CheckerRegistryData &Data, ArrayRef<std::string> Plugins,
53 DiagnosticsEngine &Diags, AnalyzerOptions &AnOpts,
54 ArrayRef<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns)
55 : Data(Data), Diags(Diags), AnOpts(AnOpts) {
// 省略 ...
123 #define GET_CHECKER_WEAK_DEPENDENCIES
124
125 #define CHECKER_WEAK_DEPENDENCY(FULLNAME, DEPENDENCY) \
126 addWeakDependency(FULLNAME, DEPENDENCY);
// 省略 ...
140 #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
// 省略 ...
143 #undef CHECKER_WEAK_DEPENDENCY
144 #undef GET_CHECKER_WEAK_DEPENDENCIES
// 省略 ...
188 }
从上面的代码可以看出,如果在CheckerRegistry::CheckerRegistry()
构造函数中调用CHECKER_WEAK_DEPENDENCY
宏,那么实质上就是调用CheckerRegistry::addWeakDependency()
函数。
而Checkers.inc
文件中仅包含如下几个CHECKER_WEAK_DEPENDENCY
宏调用。内容如下:
// 省略 ...
318 #ifdef GET_CHECKER_WEAK_DEPENDENCIES
319 CHECKER_WEAK_DEPENDENCY("alpha.unix.StdCLibraryFunctionArgs", "core.CallAndMessage")
320 CHECKER_WEAK_DEPENDENCY("alpha.unix.StdCLibraryFunctionArgs", "core.NonNullParamChecker")
321 CHECKER_WEAK_DEPENDENCY("alpha.unix.StdCLibraryFunctionArgs", "alpha.unix.Stream")
322
323 #endif // GET_CHECKER_WEAK_DEPENDENCIES
// 省略 ...
因此,函数CheckerRegistry::addWeakDependency()
的参数值直接来自于Checkers.inc
文件,并且是由Checkers.td
文件的内容决定的。
Checkers.inc:319 行的宏定义表示:检查器alpha.unix.StdCLibraryFunctionArgs
弱依赖于检查器core.CallAndMessage
。
因此,CHECKER_WEAK_DEPENDENCY
宏定义的作用为:用于指定检查器之间的弱依赖关系。该宏的两个参数都表示检查器的全称,并且前者直接弱依赖于后者。
因此, 结构体CheckerRegistryData
中的数据成员WeakDependencies
的作用为:用于存储检查器之间的直接弱依赖关系。其元素的first
字段表示一个检查器的全称,second
字段表示其直接弱依赖的检查器的全称。
step 1: 成员函数getMutableCheckersForCmdLineArg()
待补充
结构体CheckerRegistryData
的作用为: 用于封装由Checkers.td
文件所描述的检查器及其相关数据。
其数据成员及用途如下:
数据成员 | 数据类型 | 用途 |
---|---|---|
EnabledCheckers | CheckerInfoSet |
用于存储所有启用的检查器及其所有依赖的检查器。依赖的检查器包括如下几种: |
Checkers | CheckerInfoList |
用于存储所有可以被注册的检查器的相关数据。这些检查器正是Checkers.td 文件中所描述的那些检查器。 |
Packages | PackageInfoList |
? |
PackageSizes | llvm::StringMap<size_t> |
? |
PackageOptions | llvm::SmallVector<std::pair<StringRef, StringRef>, 0> |
? |
CheckerOptions | llvm::SmallVector<std::pair<StringRef, StringRef>, 0> |
? |
Dependencies | llvm::SmallVector<std::pair<StringRef, StringRef>, 0> |
用于存储检查器之间的直接强依赖关系(这些依赖关系是由Checkers.td 文件的内容决定的)。其元素的first 字段表示一个检查器的全称,second 字段表示其直接强依赖的检查器的全称。 |
WeakDependencies | llvm::SmallVector<std::pair<StringRef, StringRef>, 0> |
用于存储检查器之间的直接弱依赖关系(这些依赖关系是由Checkers.td 文件的内容决定的)。其元素的first 字段表示一个检查器的全称,second 字段表示其直接弱依赖的检查器的全称。 |
其成员函数及用途如下:
成员函数 | 函数原型 | 用途 |
---|---|---|
getMutableCheckersForCmdLineArg() | CheckerInfoListRange getMutableCheckersForCmdLineArg(StringRef CmdLineArg); | ? |
下一篇:LLVM 之 Clang 源码分析篇(3):clang::ento::CheckerRegistry 类