Clang 如何使用铿锵';s静态分析仪?

Clang 如何使用铿锵';s静态分析仪?,clang,static-analysis,clang-static-analyzer,Clang,Static Analysis,Clang Static Analyzer,假设我正在使用以下C代码段: void inc(int *num) {*num++;} void dec(int *num) {*num--;} void f(int var) { inc(&var); dec(&var); } 通过使用静态分析器,我想知道var的值在函数执行期间是否没有改变。我知道我必须自己保持它的状态(这就是编写铿锵检查器的目的),但是我很难获得这个变量的唯一引用 例如:如果我使用以下API void MySimpleChecker::c

假设我正在使用以下C代码段:

void inc(int *num) {*num++;}
void dec(int *num) {*num--;}

void f(int var) {
    inc(&var);
    dec(&var);
}
通过使用静态分析器,我想知道var的值在函数执行期间是否没有改变。我知道我必须自己保持它的状态(这就是编写铿锵检查器的目的),但是我很难获得这个变量的唯一引用

例如:如果我使用以下API

void MySimpleChecker::checkPostCall(const CallEvent &Call,
                                    CheckerContext &C) const {
    SymbolRef MyArg = Call.getArgSVal(0).getAsSymbol();
}
我希望它返回一个指针,指向我的检查器上下文中该符号的表示。但是,我总是以这种方式将0输入MyArg。在预回调和后回调中,inc和dec函数都会发生这种情况

我错过了什么?我弄错了什么概念


注:我目前正在阅读,我已经阅读了优秀的材料。到目前为止,我仍然找不到我的答案。

我认为您没有检查到此调用事件是对您的function inc/dec的调用。您应该

void MySimpleChecker::checkPostCall(const CallEvent &Call,
                                CheckerContext &C) const {
    const IdentifierInfo* callee = Call.getCalleeIdentifier();
    if (callee->getName().str() == "inc" || callee->getName().str() == "dec")
        SymbolRef MyArg = Call.getArgSVal(0).getAsSymbol();
}
问题的解释 具体来说,您需要计算应用于每个变量的对
inc
dec
的调用,并报告它们在函数中的某些路径上不平衡的情况

通常,您希望知道如何将抽象值(这里是一个数字)与程序变量关联,并能够沿每个执行路径更新和查询该值

高级答案 虽然教程检查器将抽象值与存储在变量中的值相关联,但这里我们希望将抽象值与变量本身相关联。这就是跟踪容器时所做的,因此我基于它来制定解决方案

在静态分析器的抽象状态中,每个变量都由一个对象表示。因此,第一步是制作一张地图,其中
MemRegion
是关键:

用程序状态注册映射(TrackVarMap,MemRegion const*,int)
接下来,当我们有一个与变量指针相对应的变量时,我们可以使用来获取相应的
MemRegion
。例如,给定一个,
调用
,第一个参数是指针,我们可以执行以下操作:

if(MemRegion const*region=call.getArgSVal(0.getAsRegion()){
获取指针指向的
区域

然后,我们可以使用该
区域
作为其键访问地图:

state=state->set(区域,newValue);
最后,在中,我们用于检测区域(变量)何时超出范围:

const TrackVarMapTy&Map=state->get();
用于(自动常量和I:Map){
MemRegion const*region=I.first;
int delta=一秒;
if(SymReaper.isLiveRegion(region)| |(delta==0))
continue;//未死或未更改;跳过。
完整示例 为了演示,这里有一个完整的检查程序,它报告了
inc
dec
的不平衡使用:

//TrackVarChecker.cpp
// https://stackoverflow.com/questions/23448540/how-to-keep-track-of-a-variable-with-clangs-static-analyzer
#包括“clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h”
#包括“clang/StaticAnalyzer/Core/BugReporter/BugType.h”
#包括“clang/StaticAnalyzer/Core/Checker.h”
#包括“clang/StaticAnalyzer/Core/CheckerManager.h”
#包括“clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h”
#包括“clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h”
#包括“clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h”
#包括“clang/StaticAnalyzer/Core/PathSensitive/programstatetracit.h”
使用名称空间铿锵;
使用名称空间ento;
名称空间{
类TrackVarChecker
:公共检查器<检查::后调用,
检查::死亡符号>
{
可变标识信息*II_inc,*II_dec;
可变std::唯一ptr BT_修改;
公众:
TrackVarChecker():II_inc(nullptr),II_dec(nullptr){}
void checkPostCall(CallEvent const&Call、CheckerContext&C)const;
无效checkDeadSymbols(SymbolReaper&SymbolReaper,CheckerContext&C)常量;
};
}//结束匿名命名空间
//对应于变量(即
//变量本身,而不是其当前值)之间的差异
//当前值和原始值。
用程序状态注册映射(TrackVarMap,MemRegion const*,int)
void TrackVarChecker::checkPostCall(CallEvent const&call,CheckerContext&C)const
{
const FunctionDecl*FD=dyn_cast(调用.getDecl());
如果(!FD | | FD->getKind()!=Decl::Function){
返回;
}
ASTContext&Ctx=C.getASTContext();
如果(!II_公司){
II_inc=&Ctx.Idents.get(“inc”);
}
如果(!II_dec){
II_dec=&Ctx.Idents.get(“dec”);
}
如果(FD->getIdentifier()==II|inc | FD->getIdentifier()==II|dec){
//我们希望参数是指针。获取内存区域
//指针指向的。
if(MemRegion const*region=call.getArgSVal(0.getAsRegion()){
//增加关联的值,如果需要,首先创建它。
ProgramStateRef state=C.getState();
int delta=(FD->getIdentifier()==II_inc)?+1:-1;
int const*curp=state->get(区域);
int newValue=(curp?*curp:0)+增量;
状态=状态->设置(区域,新值);
C.过渡(国家);
}
}
}
void TrackVarChecker::checkDeadSymbols(
SymbolReaper&SymbolReaper,CheckerContext&C)常数
{
ProgramStateRef state=C.getState();
const TrackVarMapTy&Map=state->get();
用于(自动常量和I:Map){
//检查内存区域(变量)是否超出了具有
//非零增量。
MemRegion const*region=I.first;
int delta=一秒;
if(SymReaper.isLiveRegion(region)| |(delta==0)){
continue;//未死或未更改;跳过。
}

//llvm::errs()您可以提供实现的更多详细信息吗?您的方法似乎没有任何问题。可能您的实现存在一些问题。我使用SimpleStreamChecker作为参考。在checkPostCall方法中,我已注释掉