Как отслеживать переменную с помощью статического анализатора Clang?

Метод parquetFile метода SQLContext и паркета DataFrameReader принимает несколько путей. Поэтому любая из этих работ:

df = sqlContext.parquetFile('/dir1/dir1_2', '/dir2/dir2_1')

или

df = sqlContext.read.parquet('/dir1/dir1_2', '/dir2/dir2_1')

28
задан ivarec 3 May 2014 в 19:07
поделиться

2 ответа

Интерпретация вопроса

А именно, Вы хотите считать вызовы к inc, и dec относился к каждой переменной, и сообщите, когда они не балансируются для некоторого пути в функции.

Обычно Вы хотите знать, как связать абстрактное значение, здесь число, с переменной программы, и быть в состоянии обновить и запросить то значение вдоль каждого пути выполнения.

Высокоуровневый ответ

принимая во внимание, что учебное средство проверки SimpleStreamChecker.cpp партнеры абстрактное значение со значением сохранило в [1 134] переменная, здесь мы хотим партнера абстрактное значение с самой переменной. Именно это IteratorChecker.cpp делает при отслеживании контейнеров, таким образом, я основывал свое решение на нем.

В абстрактном состоянии статического анализатора, каждая переменная представлена MemRegion объект. Таким образом, первый шаг должен сделать карту, где MemRegion ключ:

REGISTER_MAP_WITH_PROGRAMSTATE(TrackVarMap, MemRegion const *, int)

Затем, когда мы имеем SVal , который соответствует указателю на переменную, мы можем использовать SVal::getAsRegion для получения соответствия MemRegion. Например, учитывая CallEvent , call, с первым аргументом, который является указателем, мы можем сделать:

    if (MemRegion const *region = call.getArgSVal(0).getAsRegion()) {

для получения region, на который указывает указатель.

Затем мы можем получить доступ к нашей карте с помощью этого region как ее ключ:

      state = state->set<TrackVarMap>(region, newValue);

Наконец, в [1 132] checkDeadSymbols , мы используем SymbolReaper::isLiveRegion для обнаружения, когда регион (переменная) выходит из объема:

  const TrackVarMapTy &Map = state->get<TrackVarMap>();
  for (auto const &I : Map) {
    MemRegion const *region = I.first;
    int delta = I.second;
    if (SymReaper.isLiveRegion(region) || (delta==0))
      continue;              // Not dead, or unchanged; skip.

Полный пример

Для демонстрации вот полное средство проверки, которое сообщает о несбалансированном использовании [1 122] и dec:

// TrackVarChecker.cpp
// https://stackoverflow.com/questions/23448540/how-to-keep-track-of-a-variable-with-clangs-static-analyzer

#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"

using namespace clang;
using namespace ento;

namespace {
class TrackVarChecker
  : public Checker< check::PostCall,
                    check::DeadSymbols >
{
  mutable IdentifierInfo *II_inc, *II_dec;
  mutable std::unique_ptr<BuiltinBug> BT_modified;

public:
  TrackVarChecker() : II_inc(nullptr), II_dec(nullptr) {}

  void checkPostCall(CallEvent const &Call, CheckerContext &C) const;
  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
};
} // end anonymous namespace

// Map from memory region corresponding to a variable (that is, the
// variable itself, not its current value) to the difference between its
// current and original value.
REGISTER_MAP_WITH_PROGRAMSTATE(TrackVarMap, MemRegion const *, int)

void TrackVarChecker::checkPostCall(CallEvent const &call, CheckerContext &C) const
{
  const FunctionDecl *FD = dyn_cast<FunctionDecl>(call.getDecl());
  if (!FD || FD->getKind() != Decl::Function) {
    return;
  }

  ASTContext &Ctx = C.getASTContext();
  if (!II_inc) {
    II_inc = &Ctx.Idents.get("inc");
  }
  if (!II_dec) {
    II_dec = &Ctx.Idents.get("dec");
  }

  if (FD->getIdentifier() == II_inc || FD->getIdentifier() == II_dec) {
    // We expect the argument to be a pointer.  Get the memory region
    // that the pointer points at.
    if (MemRegion const *region = call.getArgSVal(0).getAsRegion()) {
      // Increment the associated value, creating it first if needed.
      ProgramStateRef state = C.getState();
      int delta = (FD->getIdentifier() == II_inc)? +1 : -1;
      int const *curp = state->get<TrackVarMap>(region);
      int newValue = (curp? *curp : 0) + delta;
      state = state->set<TrackVarMap>(region, newValue);
      C.addTransition(state);
    }
  }
}

void TrackVarChecker::checkDeadSymbols(
  SymbolReaper &SymReaper, CheckerContext &C) const
{
  ProgramStateRef state = C.getState();
  const TrackVarMapTy &Map = state->get<TrackVarMap>();
  for (auto const &I : Map) {
    // Check for a memory region (variable) going out of scope that has
    // a non-zero delta.
    MemRegion const *region = I.first;
    int delta = I.second;
    if (SymReaper.isLiveRegion(region) || (delta==0)) {
      continue;              // Not dead, or unchanged; skip.
    }

    //llvm::errs() << region << " dead with delta " << delta << "\n";
    if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
      if (!BT_modified) {
        BT_modified.reset(
          new BuiltinBug(this, "Delta not zero",
                         "Variable changed from its original value."));
      }
      C.emitReport(llvm::make_unique<BugReport>(
        *BT_modified, BT_modified->getDescription(), N));
    }
  }
}

void ento::registerTrackVarChecker(CheckerManager &mgr) {
  mgr.registerChecker<TrackVarChecker>();
}

bool ento::shouldRegisterTrackVarChecker(const LangOptions &LO) {
  return true;
}

Для сцепления этого в с остальной частью Лязга добавьте записи в:

  • clang/include/clang/StaticAnalyzer/Checkers/Checkers.td и
  • clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt

вход В качестве примера для тестирования его:

// trackvar.c
// Test for TrackVarChecker.

// The behavior of these functions is hardcoded in the checker.
void inc(int *num);
void dec(int *num);

void call_inc(int var) {
  inc(&var);
} // reported

void call_inc_dec(int var) {
  inc(&var);
  dec(&var);
} // NOT reported

void if_inc(int var) {
  if (var > 2) {
    inc(&var);
  }
} // reported

void indirect_inc(int val) {
  int *p = &val;
  inc(p);
} // reported

выполненный Образец:

$ gcc -E -o trackvar.i trackvar.c
$ ~/bld/llvm-project/build/bin/clang -cc1 -analyze -analyzer-checker=alpha.core.TrackVar trackvar.i
trackvar.c:10:1: warning: Variable changed from its original value
}
^
trackvar.c:21:1: warning: Variable changed from its original value
}
^
trackvar.c:26:1: warning: Variable changed from its original value
}
^
3 warnings generated.
1
ответ дан 28 November 2019 в 02:11
поделиться

Я думаю, что вы пропустили проверку, что это событие вызова является вызовом вашей функции 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();
}
0
ответ дан anirudh 3 May 2014 в 19:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: