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的源码实现目录如下:


数据成员

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

从上面的结果可以看出:

那么,这些函数参数的值是从何而来的呢?

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<charstd::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<charstd::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);

    References


    下一篇:LLVM 之 Clang 源码分析篇(3):clang::ento::CheckerRegistry 类

    上一篇:LLVM 之 Clang 源码分析篇(1):clang::ento::CheckerInfo 结构体

    首页