LLVM 之 Clang 源码分析篇(4):clang::ento::CheckerManager 类
Author: stormQ
Created: Sunday, 18. April 2021 02:18PM
Last Modified: Thursday, 22. April 2021 08:11AM
本文基于release/12.x
版本的 LLVM 源码,研究了clang::ento::CheckerManager
类中各数据成员表示的意义以及成员函数的作用。从而,有助于理解 Clang 静态分析器源码实现的其他相关部分。
类clang::ento::CheckerManager
的源码实现目录如下:
头文件为clang/include/clang/StaticAnalyzer/Core/CheckerManager.h
源文件为clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
step 1: 数据成员CheckerTags
数据成员CheckerTags
的定义如下(定义在 clang/include/clang/StaticAnalyzer/Core/CheckerManager.h 文件中):
627 llvm::DenseMap<CheckerTag, CheckerRef> CheckerTags;
数据类型CheckerTag
的定义如下(定义在 clang/include/clang/StaticAnalyzer/Core/CheckerManager.h 文件中):
192 using CheckerTag = const void *;
数据类型CheckerRef
的定义如下(定义在 clang/include/clang/StaticAnalyzer/Core/CheckerManager.h 文件中):
191 using CheckerRef = CheckerBase *;
因此,数据成员CheckerTags
的实际定义如下:
llvm::DenseMap<const void *, CheckerBase *> CheckerTags;
通过搜索源码发现,成员函数registerChecker()
中会设置该数据成员的值。其源码实现如下(定义在 clang/include/clang/StaticAnalyzer/Core/CheckerManager.h 文件中):
204 template <typename CHECKER, typename... AT>
205 CHECKER *registerChecker(AT &&... Args) {
206 CheckerTag tag = getTag<CHECKER>();
207 CheckerRef &ref = CheckerTags[tag];
208 assert(!ref && "Checker already registered, use getChecker!");
209
210 CHECKER *checker = new CHECKER(std::forward<AT>(Args)...);
// 省略 ...
214 ref = checker;
215 return checker;
216 }
从上面的代码可以看出,数据成员CheckerTags
中元素的key
值即成员函数getTag()
的函数返回值,value
值是一个指向检查器类实例的指针。
由于成员函数getTag()
的作用为:为每个检查器类生成唯一的标识符。
因此,类CheckerManager
中的数据成员CheckerTags
的作用为:用于存储已创建的检查器类实例。其元素的key
值表示检查器类实例的唯一标识符,value
值表示检查器类实例的内存地址。
step 2: 数据成员CurrentCheckerName
数据成员CurrentCheckerName
的定义如下:
132 CheckerNameRef CurrentCheckerName;
数据类型CheckerNameRef
的定义如下(定义在 clang/include/clang/StaticAnalyzer/Core/CheckerManager.h 文件中):
107 class CheckerNameRef {
108 friend class ::clang::ento::CheckerRegistry;
109
110 StringRef Name;
111
112 explicit CheckerNameRef(StringRef Name) : Name(Name) {}
113
114 public:
115 CheckerNameRef() = default;
116
117 StringRef getName() const { return Name; }
118 operator StringRef() const { return Name; }
119 };
通过搜索源码发现,成员函数setCurrentCheckerName()
中会设置该数据成员的值。其源码实现如下(定义在 clang/include/clang/StaticAnalyzer/Core/CheckerManager.h 文件中):
163 void setCurrentCheckerName(CheckerNameRef name) { CurrentCheckerName = name; }
通过搜索源码发现,仅在CheckerRegistry::initializeManager()
函数中会调用该成员函数。其源码实现如下(定义在 clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp 文件中):
462 void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) const {
463 // Initialize the CheckerManager with all enabled checkers.
464 for (const auto *Checker : Data.EnabledCheckers) {
465 CheckerMgr.setCurrentCheckerName(CheckerNameRef(Checker->FullName));
466 Checker->Initialize(CheckerMgr);
467 }
468 }
结构体clang::ento::CheckerInfo
中的数据成员FullName
的作用为:表示检查器的全称(包括检查器所属包的包名),
注:这里直接给出了结论,具体研究过程可参考笔者的另一篇文章:《LLVM 之 Clang 源码分析篇(1):clang::ento::CheckerInfo 结构体》。
因此,类CheckerManager
中的数据成员CurrentCheckerName
的作用为:表示检查器的全称。
step 3: 数据成员CheckerDtors
数据成员CheckerDtors
的定义如下:
629 std::vector<CheckerDtor> CheckerDtors;
从上面的代码可以看出,数据成员CheckerDtors
是一个std::vector
向量,其元素的数据类型为CheckerDtor
。
数据类型CheckerDtor
的定义如下(定义在 clang/include/clang/StaticAnalyzer/Core/CheckerManager.h 文件中):
193 using CheckerDtor = CheckerFn<void ()>;
因此,数据成员CheckerDtors
的实际定义如下:
std::vector<clang::ento::CheckerFn<void()>> CheckerDtors;
通过搜索源码发现,成员函数registerChecker()
中会设置该数据成员的值。其源码实现如下(定义在 clang/include/clang/StaticAnalyzer/Core/CheckerManager.h 文件中):
204 template <typename CHECKER, typename... AT>
205 CHECKER *registerChecker(AT &&... Args) {
// 省略 ...
210 CHECKER *checker = new CHECKER(std::forward<AT>(Args)...);
// 省略 ...
212 CheckerDtors.push_back(CheckerDtor(checker, destruct<CHECKER>));
// 省略 ...
216 }
从上面的代码可以看出,调用语句CheckerDtor(checker, destruct<CHECKER>)
实质上就是调用CheckerFn<void ()>(checker, destruct<CHECKER>)
。因此,模板类CheckerFn
的构造函数会被调用。
模板类CheckerFn
的定义如下(定义在 clang/include/clang/StaticAnalyzer/Core/CheckerManager.h 文件中):
61 template <typename RET, typename... Ps>
62 class CheckerFn<RET(Ps...)> {
63 using Func = RET (*)(void *, Ps...);
64
65 Func Fn;
66
67 public:
68 CheckerBase *Checker;
69
70 CheckerFn(CheckerBase *checker, Func fn) : Fn(fn), Checker(checker) {}
71
72 RET operator()(Ps... ps) const {
73 return Fn(Checker, ps...);
74 }
75 };
从上面的代码可以看出,模板类CheckerFn
的构造函数有两个参数,参数checker
是一个指向检查器类实例的指针,参数fn
是一个函数指针。这里,参数fn
的值就是成员函数destruct()
的内存地址。
由于成员函数destruct()
的作用为:销毁指定的检查器类实例。
因此,类CheckerManager
中的数据成员CheckerDtors
的作用为:用于存储所有的检查器类实例及其销毁函数。
step 4: 数据成员PreCallCheckers
数据成员PreCallCheckers
的定义如下:
666 std::vector<CheckCallFunc> PreCallCheckers;
从上面的代码可以看出,数据成员PreCallCheckers
是一个std::vector
向量,其元素的数据类型为CheckCallFunc
。
数据类型CheckCallFunc
的定义如下(定义在 clang/include/clang/StaticAnalyzer/Core/CheckerManager.h 文件中):
489 using CheckCallFunc =
490 CheckerFn<void (const CallEvent &, CheckerContext &)>;
因此,数据成员PreCallCheckers
的实际定义如下:
std::vector<clang::ento::CheckerFn<void(const clang::ento::CallEvent&, clang::ento::CheckerContext&)>> PreCallCheckers;
通过搜索源码发现,成员函数_registerForPreCall()
中会设置该数据成员的值。其源码实现如下(定义在 clang/lib/StaticAnalyzer/Core/CheckerManager.cpp 文件中):
817 void CheckerManager::_registerForPreCall(CheckCallFunc checkfn) {
818 PreCallCheckers.push_back(checkfn);
819 }
通过搜索源码发现,仅在函数clang::ento::check::PreCall::_register()
中会调用该成员函数。其源码实现如下(定义在 clang/include/clang/StaticAnalyzer/Core/Checker.h 文件中):
172 template <typename CHECKER>
173 static void _register(CHECKER *checker, CheckerManager &mgr) {
174 mgr._registerForPreCall(
175 CheckerManager::CheckCallFunc(checker, _checkCall<CHECKER>));
176 }
从上面的代码可以看出,其代码逻辑为:
首先,构造一个CheckerManager::CheckerFn
类的实例。其参数checker
的值为一个指向检查器类CHECKER
的实例的指针,参数fn
的值为clang::ento::check::PreCall::_checkCall<CHECKER>(void*, clang::ento::CallEvent const&, clang::ento::CheckerContext&)>
函数的内存地址。
然后,将其添加到数据成员CheckerManager::PreCallCheckers
中。
因此,类CheckerManager
中的数据成员PreCallCheckers
的作用为:用于存储所有订阅程序点clang::ento::check::PreCall
的检查器类实例及各自的回调函数。
需要注意的是, 这里存储的回调函数是clang::ento::check::PreCall::_checkCall<CHECKER>()
,而不是具象检查器类实现的回调函数checkPreCall()
。
step 1: 成员函数getTag()
该函数的源码实现如下(定义在 clang/include/clang/StaticAnalyzer/Core/CheckerManager.h 文件中):
624 template <typename T>
625 static void *getTag() { static int tag; return &tag; }
从上面的代码可以看出,其代码逻辑为:模板参数值不同时,返回不同的静态局部变量的内存地址。
通过搜索源码发现,成员函数registerChecker()
函数中会调用该成员函数。其源码实现如下(定义在 clang/include/clang/StaticAnalyzer/Core/CheckerManager.h 文件中):
204 template <typename CHECKER, typename... AT>
205 CHECKER *registerChecker(AT &&... Args) {
206 CheckerTag tag = getTag<CHECKER>();
// 省略 ...
216 }
从上面的代码可以看出,在实际使用时成员函数registerChecker()
的模板参数为检查器类。
因此,类CheckerManager
中的成员函数getTag()
的作用为:为每个检查器类生成唯一的标识符。
step 2: 成员函数destruct()
该函数的源码实现如下(定义在 clang/include/clang/StaticAnalyzer/Core/CheckerManager.h 文件中):
621 template <typename CHECKER>
622 static void destruct(void *obj) { delete static_cast<CHECKER *>(obj); }
从上面的代码可以看出,其代码逻辑为:销毁一个由模板参数指定的类的实例。
通过搜索源码发现,成员函数registerChecker()
函数中会调用该成员函数。其源码实现如下(定义在 clang/include/clang/StaticAnalyzer/Core/CheckerManager.h 文件中):
204 template <typename CHECKER, typename... AT>
205 CHECKER *registerChecker(AT &&... Args) {
// 省略 ...
210 CHECKER *checker = new CHECKER(std::forward<AT>(Args)...);
// 省略 ...
212 CheckerDtors.push_back(CheckerDtor(checker, destruct<CHECKER>));
// 省略 ...
216 }
从上面的代码可以看出,在实际使用时成员函数destruct()
的模板参数为检查器类。
因此,类CheckerManager
中的成员函数destruct()
的作用为:销毁指定的检查器类实例。
step 3: 成员函数registerChecker()
该函数的源码实现如下(定义在 clang/include/clang/StaticAnalyzer/Core/CheckerManager.h 文件中):
199 /// Used to register checkers.
200 /// All arguments are automatically passed through to the checker
201 /// constructor.
202 ///
203 /// \returns a pointer to the checker object.
204 template <typename CHECKER, typename... AT>
205 CHECKER *registerChecker(AT &&... Args) {
206 CheckerTag tag = getTag<CHECKER>();
207 CheckerRef &ref = CheckerTags[tag];
208 assert(!ref && "Checker already registered, use getChecker!");
209
210 CHECKER *checker = new CHECKER(std::forward<AT>(Args)...);
211 checker->Name = CurrentCheckerName;
212 CheckerDtors.push_back(CheckerDtor(checker, destruct<CHECKER>));
213 CHECKER::_register(checker, *this);
214 ref = checker;
215 return checker;
216 }
类模板clang::ento::Checker<CHECK1, CHECKs>
中的成员函数_register()
的作用为:将检查器实例和其实现的各回调函数成对地注册到检查器管理类中。
注:这里直接给出了结论,具体研究过程可参考笔者的另一篇文章:《LLVM 之 Clang 源码分析篇(5):clang::ento::Checker< CHECK1, CHECKs > 类模板》。
因此,成员函数registerChecker()
的代码逻辑为:
首先,判断该检查器类CHECKER
的实例是否已创建。如果已创建,则触发断言,从而导致该函数异常结束;否则,继续下面的流程。
接下来,创建一个检查器类CHECKER
的实例,并为其设置检查器的全称。
添加该检查器类实例和其销毁函数。
将检查器实例和其实现的各回调函数成对地注册到检查器管理类中。
存储已创建的检查器类实例,从而避免重复创建。最后,返回指向该检查器类实例的指针。
因此,类clang::ento::CheckerManager
中的成员函数registerChecker()
的作用为:创建指定的检查器类实例,并注册其订阅的程序点。
step 4: 成员函数_registerForPreCall()
该函数的源码实现如下(定义在 clang/lib/StaticAnalyzer/Core/CheckerManager.cpp 文件中):
817 void CheckerManager::_registerForPreCall(CheckCallFunc checkfn) {
818 PreCallCheckers.push_back(checkfn);
819 }
由于类CheckerManager
中的数据成员PreCallCheckers
的作用为:用于存储所有订阅程序点clang::ento::check::PreCall
的检查器类实例及各自的回调函数。
因此,类clang::ento::CheckerManager
中的成员函数_registerForPreCall()
的作用为:添加订阅程序点clang::ento::check::PreCall
的检查器类实例及其回调函数。
类clang::ento::CheckerManager
的作用为: 作为检查器管理类。
其数据成员及用途如下:
数据成员 | 数据类型 | 用途 |
---|---|---|
CheckerTags | llvm::DenseMap<CheckerTag, CheckerRef> |
用于存储已创建的检查器类实例。其元素的key 值表示检查器类实例的唯一标识符,value 值表示检查器类实例的内存地址。 |
CurrentCheckerName | CheckerNameRef |
表示检查器的全称 |
CheckerDtors | std::vector<CheckerDtor> |
用于存储所有的检查器类实例及其销毁函数 |
PreCallCheckers | std::vector<CheckCallFunc> |
用于存储所有订阅程序点clang::ento::check::PreCall 的检查器类实例及各自的回调函数 |
其成员函数及用途如下:
成员函数 | 函数原型 | 用途 |
---|---|---|
getTag() | template <typename T> static void *getTag() |
为每个检查器类生成唯一的标识符 |
destruct() | template <typename CHECKER> static void destruct(void *obj) |
销毁指定的检查器类实例 |
registerChecker() | template <typename CHECKER, typename... AT> CHECKER *registerChecker(AT &&... Args) |
创建指定的检查器类实例,并注册其订阅的程序点 |
_registerForPreCall() | 函数原型 | 添加订阅程序点clang::ento::check::PreCall 的检查器类实例及其回调函数 |
下一篇:LLVM 之 Clang 源码分析篇(5):clang::ento::Checker< CHECK1, CHECKs > 类模板