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


数据成员

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 }

要想理解该函数的实现,我们需要先弄清楚如下几个问题:

1) 分析Data.Checkers对象

Data.Checkers对象的作用为:用于存储所有可以被注册的检查器的相关数据。这些检查器正是Checkers.td文件中所描述的那些检查器。

注:这里直接给出了结论,具体研究过程可参考笔者的另一篇文章:《LLVM 之 Clang 源码分析篇(2):clang::ento::CheckerRegistryData 结构体》。

2) 分析CheckerInfo结构体

结构体CheckerInfo的相关结论如下:

注:这里直接给出了结论,具体研究过程可参考笔者的另一篇文章:《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执行如下逻辑:

因此,模板函数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执行如下逻辑:

因此,模板函数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执行如下逻辑:

因此,上述代码的作用为:收集所有启用的检查器及其所有直接和间接强依赖的检查器。

需要注意的是, 通过命令行参数启用一个检查器时,如果存在其直接或间接强依赖的检查器被禁用,那么该检查器最终不会被启用。

成员函数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执行如下逻辑:

从上述分析可以得出,类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 结构体》。

CheckerData.EnabledCheckers对象中的元素,其数据类型为const CheckerInfo *。而结构体clang::ento::CheckerInfo中的数据成员FullNameInitialize的作用分别为:表示检查器的全称、表示检查器注册函数registerXXX()的运行时内存地址。

注:这里直接给出了结论,具体研究过程可参考笔者的另一篇文章:《LLVM 之 Clang 源码分析篇(1):clang::ento::CheckerInfo 结构体》。

因此,成员函数initializeManager()的代码逻辑为:遍历所有要启用的检查器Data.EnabledCheckers,对其中的每个检查器Checker执行如下逻辑:

从上述分析可以得出,类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的相关结论如下:

注:这里直接给出了结论,具体研究过程可参考笔者的另一篇文章:《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) 对于所有直接弱依赖的检查器,如果检查器满足所指定的条件并且其所有直接和间接强依赖的检查器都满足该条件,那么将这些检查器收集起来。

    References


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

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

    首页