LLVM 之 Clang 源码分析篇(3):clang::ento::CheckerRegistry 类
Author: stormQ
Created: Thursday, 15. April 2021 00:11AM
Last Modified: Tuesday, 20. April 2021 07:20PM
本文基于release/12.x
版本的 LLVM 源码,研究了clang::ento::CheckerRegistry
类中各数据成员表示的意义以及成员函数的作用。从而,有助于理解 Clang 静态分析器源码实现的其他相关部分。
类clang::ento::CheckerRegistry
的源码实现目录如下:
头文件为clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h
源文件为clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
step 1: 数据成员Data
Data
是类CheckerRegistry
的数据成员之一。其定义如下:
190 CheckerRegistryData &Data;
从上面的代码可以看出,数据成员Data
的数据类型为CheckerRegistryData &
。
而结构体clang::ento::CheckerRegistryData
的作用为:用于封装由Checkers.td
文件所描述的检查器及其相关数据。
注:这里直接给出了结论,具体研究过程可参考笔者的另一篇文章:《LLVM 之 Clang 源码分析篇(2):clang::ento::CheckerRegistryData 结构体》。
因此,类CheckerRegistry
中的数据成员Data
的作用为:用于存储由Checkers.td
文件所描述的检查器及其相关数据。
step 2: 数据成员Diags
待补充
step 3: 数据成员AnOpts
待补充
step 1: 成员函数initializeRegistry()
该函数的源码实现如下:
206 void CheckerRegistry::initializeRegistry(const CheckerManager &Mgr) {
207 // First, we calculate the list of enabled checkers as specified by the
208 // invocation. Weak dependencies will not enable their unspecified strong
209 // depenencies, but its only after resolving strong dependencies for all
210 // checkers when we know whether they will be enabled.
211 CheckerInfoSet Tmp;
212 auto IsEnabledFromCmdLine = [&](const CheckerInfo *Checker) {
213 return !Checker->isDisabled(Mgr);
214 };
215 for (const CheckerInfo &Checker : Data.Checkers) {
216 if (!Checker.isEnabled(Mgr))
217 continue;
218
219 CheckerInfoSet Deps;
220 if (!collectStrongDependencies(Checker.Dependencies, Mgr, Deps,
221 IsEnabledFromCmdLine)) {
222 // If we failed to enable any of the dependencies, don't enable this
223 // checker.
224 continue;
225 }
226
227 Tmp.insert(Deps.begin(), Deps.end());
228
229 // Enable the checker.
230 Tmp.insert(&Checker);
231 }
232
233 // Calculate enabled checkers with the correct registration order. As this is
234 // done recursively, its arguably cheaper, but for sure less error prone to
235 // recalculate from scratch.
236 auto IsEnabled = [&](const CheckerInfo *Checker) {
237 return llvm::is_contained(Tmp, Checker);
238 };
239 for (const CheckerInfo &Checker : Data.Checkers) {
240 if (!Checker.isEnabled(Mgr))
241 continue;
242
243 CheckerInfoSet Deps;
244
245 collectWeakDependencies(Checker.WeakDependencies, Mgr, Deps, IsEnabled);
246
247 if (!collectStrongDependencies(Checker.Dependencies, Mgr, Deps,
248 IsEnabledFromCmdLine)) {
249 // If we failed to enable any of the dependencies, don't enable this
250 // checker.
251 continue;
252 }
253
254 // Note that set_union also preserves the order of insertion.
255 Data.EnabledCheckers.set_union(Deps);
256 Data.EnabledCheckers.insert(&Checker);
257 }
258 }
要想理解该函数的实现,我们需要先弄清楚如下几个问题:
Data.Checkers
对象中的数据是什么以及从何而来的?
结构体CheckerInfo
的成员函数isEnabled()
和isDisabled()
的作用?
结构体CheckerInfo
的数据成员Dependencies
和WeakDependencies
的作用以及从何而来的?
模板函数collectStrongDependencies()
和collectWeakDependencies()
的作用?
1) 分析Data.Checkers
对象
Data.Checkers
对象的作用为:用于存储所有可以被注册的检查器的相关数据。这些检查器正是Checkers.td
文件中所描述的那些检查器。
注:这里直接给出了结论,具体研究过程可参考笔者的另一篇文章:《LLVM 之 Clang 源码分析篇(2):clang::ento::CheckerRegistryData 结构体》。
2) 分析CheckerInfo
结构体
结构体CheckerInfo
的相关结论如下:
成员函数isEnabled()
的作用为:判断是否启用了检查器。返回值为true
,表示通过-analyzer-checker
标志显式地启用了检查器并且检查器注册函数shouldRegisterXXX
的返回值为true
。
成员函数isDisabled()
的作用为:判断是否禁用了检查器。返回值为true
,表示通过-analyzer-disable-checker
标志显式地禁用了检查器或者检查器注册函数shouldRegisterXXX
的返回值为false
。
数据成员Dependencies
的作用为:用于存放直接强依赖的检查器有哪些。
数据成员WeakDependencies
的作用为:用于存放直接弱依赖的检查器有哪些。
注:这里直接给出了结论,具体研究过程可参考笔者的另一篇文章:《LLVM 之 Clang 源码分析篇(1):clang::ento::CheckerInfo 结构体》。
3) 分析collectStrongDependencies()
模板函数
该函数的源码实现如下(定义在 clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp 文件中):
260 template <typename IsEnabledFn>
261 static bool collectStrongDependencies(const ConstCheckerInfoList &Deps,
262 const CheckerManager &Mgr,
263 CheckerInfoSet &Ret,
264 IsEnabledFn IsEnabled) {
265
266 for (const CheckerInfo *Dependency : Deps) {
267 if (!IsEnabled(Dependency))
268 return false;
269
270 // Collect dependencies recursively.
271 if (!collectStrongDependencies(Dependency->Dependencies, Mgr, Ret,
272 IsEnabled))
273 return false;
274 Ret.insert(Dependency);
275 }
276
277 return true;
278 }
从上面的代码可以看出,模板函数collectStrongDependencies()
的代码逻辑为:遍历直接强依赖的检查器,对每个直接强依赖的检查器Dependency
执行如下逻辑:
首先,判断检查器Dependency
是否满足指定的条件。如果不满足,那么返回false
,从而导致模板函数collectStrongDependencies()
异常结束,收集结果为空;否则,继续下面的流程。
接下来,递归收集检查器Dependency
直接强依赖的检查器。
最后,如果上述递归收集过程从未出错,那么将检查器Dependency
放入收集结果中,模板函数collectStrongDependencies()
正常结束。
因此,模板函数collectStrongDependencies()
的作用为:收集满足指定条件的所有直接和间接强依赖的检查器。返回值为true
,表示不存在强依赖的检查器或者所有直接和间接强依赖的检查器都满足所指定的条件。
4) 分析collectWeakDependencies()
模板函数
该函数的源码实现如下(定义在 clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp 文件中):
280 template <typename IsEnabledFn>
281 static void collectWeakDependencies(const ConstCheckerInfoList &WeakDeps,
282 const CheckerManager &Mgr,
283 CheckerInfoSet &Ret,
284 IsEnabledFn IsEnabled) {
285
286 for (const CheckerInfo *Dependency : WeakDeps) {
287 // Don't enable this checker if strong dependencies are unsatisfied, but
288 // assume that weak dependencies are transitive.
289 collectWeakDependencies(Dependency->WeakDependencies, Mgr, Ret, IsEnabled);
290
291 if (IsEnabled(Dependency) &&
292 collectStrongDependencies(Dependency->Dependencies, Mgr, Ret,
293 IsEnabled))
294 Ret.insert(Dependency);
295 }
296 }
从上面的代码可以看出,模板函数collectWeakDependencies()
的代码逻辑为:遍历直接弱依赖的检查器,对每个直接弱依赖的检查器Dependency
执行如下逻辑:
首先,递归收集检查器Dependency
直接弱依赖的检查器。
接下来,如果检查器Dependency
满足所指定的条件并且检查器Dependency
所有直接和间接强依赖的检查器收集成功,那么将检查器Dependency
放入收集结果中。
因此,模板函数collectWeakDependencies()
的作用为:对于所有直接弱依赖的检查器,如果检查器满足所指定的条件并且其所有直接和间接强依赖的检查器都满足该条件,那么将这些检查器收集起来。
5) 成员函数initializeRegistry()
的作用
成员函数initializeRegistry()
中第 211 行~第 231 行的代码如下:
211 CheckerInfoSet Tmp;
212 auto IsEnabledFromCmdLine = [&](const CheckerInfo *Checker) {
213 return !Checker->isDisabled(Mgr);
214 };
215 for (const CheckerInfo &Checker : Data.Checkers) {
216 if (!Checker.isEnabled(Mgr))
217 continue;
218
219 CheckerInfoSet Deps;
220 if (!collectStrongDependencies(Checker.Dependencies, Mgr, Deps,
221 IsEnabledFromCmdLine)) {
222 // If we failed to enable any of the dependencies, don't enable this
223 // checker.
224 continue;
225 }
226
227 Tmp.insert(Deps.begin(), Deps.end());
228
229 // Enable the checker.
230 Tmp.insert(&Checker);
231 }
上述代码的代码逻辑为:遍历Checkers.td
文件中所描述的那些检查器,对每个检查器Checker
执行如下逻辑:
首先,判断检查器Checker
是否被启用了。如果未被启用,那么跳过该检查器,并进行下一个检查器的收集;否则,继续下面的流程。
接下来,收集检查器Checker
所有直接和间接强依赖的检查器。如果检查器Checker
没有强依赖的检查器或者其所有直接和间接强依赖的检查器都未被禁用,那么收集成功,继续下面的流程;否则,收集失败,跳过该检查器,并进行下一个检查器的收集。
最后,将所有强依赖的检查器Deps
添加到Tmp
集合中,再将检查器Checker
添加到Tmp
集合中。
因此,上述代码的作用为:收集所有启用的检查器及其所有直接和间接强依赖的检查器。
需要注意的是, 通过命令行参数启用一个检查器时,如果存在其直接或间接强依赖的检查器被禁用,那么该检查器最终不会被启用。
成员函数initializeRegistry()
中第 236 行~第 257 行的代码如下:
236 auto IsEnabled = [&](const CheckerInfo *Checker) {
237 return llvm::is_contained(Tmp, Checker);
238 };
239 for (const CheckerInfo &Checker : Data.Checkers) {
240 if (!Checker.isEnabled(Mgr))
241 continue;
242
243 CheckerInfoSet Deps;
244
245 collectWeakDependencies(Checker.WeakDependencies, Mgr, Deps, IsEnabled);
246
247 if (!collectStrongDependencies(Checker.Dependencies, Mgr, Deps,
248 IsEnabledFromCmdLine)) {
249 // If we failed to enable any of the dependencies, don't enable this
250 // checker.
251 continue;
252 }
253
254 // Note that set_union also preserves the order of insertion.
255 Data.EnabledCheckers.set_union(Deps);
256 Data.EnabledCheckers.insert(&Checker);
257 }
上述代码的代码逻辑为:遍历Checkers.td
文件中所描述的那些检查器,对每个检查器Checker
执行如下逻辑:
首先,判断检查器Checker
是否被启用了。如果未被启用,那么跳过该检查器,并进行下一个检查器的收集;否则,继续下面的流程。
接下来,收集检查器Checker
所有直接和间接弱依赖的检查器,以及这些弱依赖的检查器所直接和间接强依赖的所有检查器。收集结果中的检查器一定来自于Tmp
集合。
接下来,收集检查器Checker
所有直接和间接强依赖的检查器。收集结果中的检查器也一定来自于Tmp
集合。如果收集失败,那么跳过该检查器,并进行下一个检查器的收集;否则收集成功,继续下面的流程。
最后,将收集的所有检查器依赖Deps
添加到Data.EnabledCheckers
对象中,再将检查器Checker
添加到Data.EnabledCheckers
对象中。
从上述分析可以得出,类CheckerRegistry
中的成员函数initializeRegistry()
的作用为:收集所有启用的检查器及其所有依赖的检查器。依赖的检查器包括如下几种:
直接和间接强依赖的检查器
直接和间接弱依赖的检查器
弱依赖检查器所直接和间接强依赖的检查器
step 2: 成员函数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 }
Data
是类CheckerRegistry
的数据成员之一,其数据类型为CheckerRegistryData &
。而结构体clang::ento::CheckerRegistryData
中的数据成员EnabledCheckers
的作用为:用于存储所有启用的检查器及其所有依赖的检查器。依赖的检查器包括如下几种:
直接和间接强依赖的检查器
直接和间接弱依赖的检查器
弱依赖检查器所直接和间接强依赖的检查器
注:这里直接给出了结论,具体研究过程可参考笔者的另一篇文章:《LLVM 之 Clang 源码分析篇(2):clang::ento::CheckerRegistryData 结构体》。
Checker
是Data.EnabledCheckers
对象中的元素,其数据类型为const CheckerInfo *
。而结构体clang::ento::CheckerInfo
中的数据成员FullName
和Initialize
的作用分别为:表示检查器的全称、表示检查器注册函数registerXXX()
的运行时内存地址。
注:这里直接给出了结论,具体研究过程可参考笔者的另一篇文章:《LLVM 之 Clang 源码分析篇(1):clang::ento::CheckerInfo 结构体》。
因此,成员函数initializeManager()
的代码逻辑为:遍历所有要启用的检查器Data.EnabledCheckers
,对其中的每个检查器Checker
执行如下逻辑:
首先,在检查器管理类CheckerMgr
中设置当前检查器Checker
的检查器全称。
然后,调用检查器注册函数registerXXX()
。
从上述分析可以得出,类CheckerRegistry
中的成员函数initializeManager()
的作用为:注册所有启用的检查器及其所有依赖的检查器。
step 3: 成员函数resolveDependencies()
该函数的源码实现如下(定义在 clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp 文件中):
298 template <bool IsWeak> void CheckerRegistry::resolveDependencies() {
299 for (const std::pair<StringRef, StringRef> &Entry :
300 (IsWeak ? Data.WeakDependencies : Data.Dependencies)) {
301
302 auto CheckerIt = binaryFind(Data.Checkers, Entry.first);
303 assert(CheckerIt != Data.Checkers.end() &&
304 CheckerIt->FullName == Entry.first &&
305 "Failed to find the checker while attempting to set up its "
306 "dependencies!");
307
308 auto DependencyIt = binaryFind(Data.Checkers, Entry.second);
309 assert(DependencyIt != Data.Checkers.end() &&
310 DependencyIt->FullName == Entry.second &&
311 "Failed to find the dependency of a checker!");
312
313 // We do allow diagnostics from unit test/example dependency checkers.
314 assert((DependencyIt->FullName.startswith("test") ||
315 DependencyIt->FullName.startswith("example") || IsWeak ||
316 DependencyIt->IsHidden) &&
317 "Strong dependencies are modeling checkers, and as such "
318 "non-user facing! Mark them hidden in Checkers.td!");
319
320 if (IsWeak)
321 CheckerIt->WeakDependencies.emplace_back(&*DependencyIt);
322 else
323 CheckerIt->Dependencies.emplace_back(&*DependencyIt);
324 }
325 }
结构体clang::ento::CheckerRegistryData
的相关结论如下:
数据成员WeakDependencies
,用于存储检查器之间的直接弱依赖关系(这些依赖关系是由Checkers.td
文件的内容决定的)。其元素的first
字段表示一个检查器的全称,second
字段表示其直接弱依赖的检查器的全称。
数据成员Dependencies
,用于存储检查器之间的直接强依赖关系(这些依赖关系是由Checkers.td
文件的内容决定的)。其元素的first
字段表示一个检查器的全称,second
字段表示其直接强依赖的检查器的全称。
注:这里直接给出了结论,具体研究过程可参考笔者的另一篇文章:《LLVM 之 Clang 源码分析篇(2):clang::ento::CheckerRegistryData 结构体》。
因此,成员函数resolveDependencies()
的代码逻辑为:如果模板参数的值为flase
,则遍历检查器之间的所有直接弱依赖关系,并将每个检查器直接弱依赖的检查器保存到该检查器内部。如果模板参数的值为true
,则遍历检查器之间的所有直接强依赖关系,并将每个检查器直接强依赖的检查器保存到该检查器内部。
从上述分析可以得出,类CheckerRegistry
中的成员函数resolveDependencies()
的作用为:将直接强依赖或直接弱依赖的检查器保存到检查器的内部。
类clang::ento::CheckerRegistry
的作用为: 收集所有启用的检查器及其所有依赖的检查器,并将这些检查器注册到检查器管理类中。
其数据成员及用途如下:
数据成员 | 数据类型 | 用途 |
---|---|---|
Data | CheckerRegistryData & |
用于存储由Checkers.td 文件所描述的检查器及其相关数据 |
Diags | DiagnosticsEngine & |
? |
AnOpts | AnalyzerOptions & |
? |
其成员函数及用途如下:
成员函数 | 函数原型 | 用途 |
---|---|---|
initializeRegistry() | void initializeRegistry(const CheckerManager &Mgr); |
收集所有启用的检查器及其所有依赖的检查器。依赖的检查器包括如下几种: |
initializeManager() | void initializeManager(CheckerManager &CheckerMgr) const |
注册所有启用的检查器及其所有依赖的检查器 |
resolveDependencies() | template <bool IsWeak> void resolveDependencies() |
将直接强依赖或直接弱依赖的检查器保存到检查器的内部 |
其他相关函数及用途如下:
函数 | 函数原型 | 用途 |
---|---|---|
collectStrongDependencies() | template <typename IsEnabledFn>
static bool collectStrongDependencies(const ConstCheckerInfoList &Deps,
const CheckerManager &Mgr,
CheckerInfoSet &Ret,
IsEnabledFn IsEnabled) |
收集满足指定条件的所有直接和间接强依赖的检查器。true ,表示不存在强依赖的检查器或者所有直接和间接强依赖的检查器都满足所指定的条件。 |
collectWeakDependencies() | template <typename IsEnabledFn>
static void collectWeakDependencies(const ConstCheckerInfoList &WeakDeps,
const CheckerManager &Mgr,
CheckerInfoSet &Ret,
IsEnabledFn IsEnabled) |
对于所有直接弱依赖的检查器,如果检查器满足所指定的条件并且其所有直接和间接强依赖的检查器都满足该条件,那么将这些检查器收集起来。 |
下一篇:LLVM 之 Clang 源码分析篇(4):clang::ento::CheckerManager 类
上一篇:LLVM 之 Clang 源码分析篇(2):clang::ento::CheckerRegistryData 结构体