void TransitionGraphReducer::reduceStgToStatesSatisfying(function<bool(const EState*)> predicate) {
  if (_stg->size() == 0) {
    return;
  }
  // no in-place reduction because paths in between states that satisfy the predicate might be shared
  TransitionGraph* reducedStg = new TransitionGraph();
  reducedStg->setStartLabel(_stg->getStartLabel());
  reducedStg->setIsPrecise(_stg->isPrecise());
  reducedStg->setIsComplete(_stg->isComplete());
  // init
  list<const EState*> worklist;
  unordered_set<const EState*> visited;
  worklist.push_back(_stg->getStartEState());
  visited.insert(_stg->getStartEState());
  // traverse the entire _stg
  while (!worklist.empty()) {
    const EState* current = *worklist.begin();
    ROSE_ASSERT(current);
    worklist.pop_front();
    ROSE_ASSERT(predicate(current) || current == _stg->getStartEState());
    // similar to Analyzer's "subSolver"
    list<const EState*> successors = successorsOfStateSatisfying(current, predicate);
    for (list<const EState*>::iterator i=successors.begin(); i!= successors.end(); ++i) {
      if (visited.find(*i) == visited.end()) {
	worklist.push_back(*i);
      visited.insert(*i);
      }
      Edge* newEdge = new Edge(current->label(),EDGE_PATH,(*i)->label());
      reducedStg->add(Transition(current, *newEdge, *i));
    }
  }
  // replace old stg (local variable of analyzer, stack frame) with reduced one
  *_stg = *reducedStg;
  // "garbage collection": Remove all states from _states that were bypassed during the reduction
  // (cannot simply replace the set because of potential states in the analyzer's worklist)
  EStateSet::iterator i=_states->begin();
  while (i!=_states->end()) {
    if (visited.find(*i) == visited.end()) {
      i = _states->erase(i);
    } else {
      ++i;
    }
  }
}
示例#2
0
PropertyValueTable* CounterexampleAnalyzer::cegarPrefixAnalysisForLtl(int property, SpotConnection& spotConnection, 
									set<int> ltlInAlphabet, set<int> ltlOutAlphabet) {
  // visualizer for in-depth model outputs (.dot files)
  Visualizer visualizer(_analyzer->getLabeler(),_analyzer->getVariableIdMapping(),
                          _analyzer->getFlow(),_analyzer->getPStateSet(),_analyzer->getEStateSet(),_analyzer->getTransitionGraph());
  string vizFilenamePrefix = "";
  if(args.count("viz-cegpra-detailed")) {
    vizFilenamePrefix=args["viz-cegpra-detailed"].as<string>();
    string filename = vizFilenamePrefix + "_cegpra_init.dot";
    writeDotGraphToDisk(filename, visualizer);
  }
  // OVERVIEW
  // (0) check if the initial model already satsifies the property
  // (0.5) initialization
  // (while (property != satisfied)) do
  //   (1) Disconnect the concrete prefix (initially the start state) from the over-approx. part of the model
  //   (2) Anaylze the most recent counterexample while adding the trace of the original program to the prefix part of the model
  //       If the counterexample is real: reconnect once more to determine the model's size, ESCAPE the loop and return results 
  //   (3) Reconnect both parts of the model
  //   (4) Check the property on the now slightly refined model
  // od
  // (5) return results;
  cout << "STATUS: CEGPRA is now analyzing LTL property " << property << "..." << endl;
  if (_csvOutput) {
    (*_csvOutput) << endl << property << ",";
  }
  TransitionGraph* model = _analyzer->getTransitionGraph();
  assert(model->isComplete());
  // (0) check if the given property already holds on the initial over-approximated model
  PropertyValueTable* currentResults = spotConnection.getLtlResults();
  if (currentResults->getPropertyValue(property) != PROPERTY_VALUE_UNKNOWN) {
    cout << "STATUS: property " << property << " was already analyzed. CEGAR analysis will not be started." << endl;
    return currentResults;
  }
  spotConnection.checkSingleProperty(property, *model, ltlInAlphabet, ltlOutAlphabet, true, true);
  currentResults = spotConnection.getLtlResults();
  // (0.5) prepare for the continuous tracing of concrete states (will become the prefix of a refined abstract model)
  // store connectors in the over-approx. part of the model (single row of input states in the initial "topified" model)
  const EState* startEState = model->getStartEState();
  pair<EStatePtrSet, EStatePtrSet> concOutputAndAbstrInput = getConcreteOutputAndAbstractInput(model); 
  EStatePtrSet startAndOuputStatesPrefix = concOutputAndAbstrInput.first;
  vector<const EState*> firstInputOverApprox(ltlInAlphabet.size());
  firstInputOverApprox = sortAbstractInputStates(firstInputOverApprox, concOutputAndAbstrInput.second);
  int loopCount = 0;
  bool falsified = false;
  bool verified = true;   // the usual case for the loop below to terminate is a verified property.
  string ce = "no counterexample yet";
  // as long as the property is not satisfiable yet, refine by enlarging the prefix of concrete states according to counterexamples
  while (currentResults->getPropertyValue(property) != PROPERTY_VALUE_YES) { 
    if (_maxCounterexamples > -1 && (loopCount + 1) > _maxCounterexamples) {
      verified = false;
      spotConnection.resetLtlResults(property);
      break;
    }
    loopCount++;
    if (loopCount % 50 == 0) {
      cout << "STATUS: " << loopCount << " counterexamples analyzed. most recent counterexample: " << endl;
      cout << ce << endl;
    }
    // (1) disconnect prefix and over-approx. part of the model
    model->setIsComplete(false);
    for (unsigned int i = 0; i < firstInputOverApprox.size(); i++) {
       TransitionPtrSet connectionsToPrefix = model->inEdges(firstInputOverApprox[i]);
       for (TransitionPtrSet::iterator k=connectionsToPrefix.begin(); k!=connectionsToPrefix.end(); ++k) {
         // check if predeccesor at the source of that transition is a concrete (prefix) state
         if ( !(*k)->source->isRersTopified(_analyzer->getVariableIdMapping()) ) {
           model->erase(**k);
         }
       }
    }
    model->setIsPrecise(true);
    if(args.count("viz-cegpra-detailed")) {
      stringstream filenameStream;
      filenameStream << vizFilenamePrefix << "cegpra_afterDisconnect_i" << loopCount << ".dot";
      writeDotGraphToDisk(filenameStream.str(), visualizer);
    }
    // (2) add a trace to the prefix according to the most recent counterexample. Analyze the counterexample while adding the trace.
    ce = currentResults->getCounterexample(property);
    //cout << "STATUS: counterexample: " << ce << endl;
    CEAnalysisResult ceaResult = analyzeCounterexample(ce, startEState, false, false);
    if (ceaResult.analysisResult == CE_TYPE_REAL) {
      // still reconnect the concrete prefix with the over-approx. part of the model (step (3)) in order to report the size.
      falsified = true;
      verified = false;
    } else if (ceaResult.analysisResult == CE_TYPE_SPURIOUS) {
      if(!boolOptions["keep-error-states"]) {
        // remove a trace leading to an error state and mark the branches to it (do not reconnect in phase 3) 
        removeAndMarkErroneousBranches(model);
      }
      //the trace eliminating the spurious counterexample (maybe including a few extra states) was added to the prefix during analysis.
      // --> nothing to do here
    } else {
      assert(0);  //counterexample analysis not successfully completed
    }
    if(args.count("viz-cegpra-detailed")) {
      stringstream filenameStream;
      filenameStream << vizFilenamePrefix << "cegpra_afterCECheck_i" << loopCount << ".dot";
      writeDotGraphToDisk(filenameStream.str(), visualizer);
    }
    // (3) reconnect both parts of the model
    model->setIsPrecise(false);
    //update set of output states (plus start state) in the precise prefix
    addAllPrefixOutputStates(startAndOuputStatesPrefix, model);
    for (set<const EState*>::iterator i=startAndOuputStatesPrefix.begin(); i!=startAndOuputStatesPrefix.end(); ++i) {
      vector<bool> inputSuccessors(ltlInAlphabet.size(), false);
      if(!boolOptions["keep-error-states"]) {
        inputSuccessors = setErrorBranches(inputSuccessors, *i); 
      }
      // determine which input states exist as successors in the prefix
      inputSuccessors = hasFollowingInputStates(inputSuccessors, *i, model);
      //connect with the approx. part of the model for all input values not found among the successor states
      for (unsigned int k = 0; k < inputSuccessors.size(); k++) {
        if (!inputSuccessors[k]) {
          Edge newEdge;
          model->add(Transition((*i), newEdge, firstInputOverApprox[k]));
        }
      }
    }
    model->setIsComplete(true);
    if(args.count("viz-cegpra-detailed")) {
      stringstream filenameStream;
      filenameStream << vizFilenamePrefix << "cegpra_afterReconnect_i" << loopCount << ".dot";
      writeDotGraphToDisk(filenameStream.str(), visualizer);
    }
    // if falsified: after reconnecting, leave the analysis loop, report size of the model and return the results
    if (falsified) {
      break;
    }
    // (4) check if the property holds on the refined model
    spotConnection.resetLtlResults(property);
    spotConnection.checkSingleProperty(property, *model, ltlInAlphabet, ltlOutAlphabet, true, true);
    currentResults = spotConnection.getLtlResults();
  }
  // (5) check all properties using the current model and return the result
  spotConnection.checkLtlProperties(*model, ltlInAlphabet, ltlOutAlphabet, true, false);
  currentResults = spotConnection.getLtlResults();
  printStgSizeAndCeCount(model, loopCount, property);
  if (_csvOutput) {
    if (verified && !falsified)
      (*_csvOutput) << "y,";
    if (!verified && falsified)
      (*_csvOutput) << "n,";
    if (!verified && !falsified)
      (*_csvOutput) << "?,";
    if (verified && falsified) {
      cout << "ERROR: property can not be both verified and falsified. " << endl;
      assert(0);
    }
    (*_csvOutput) << currentResults->entriesWithValue(PROPERTY_VALUE_YES)<<",";
    (*_csvOutput) << currentResults->entriesWithValue(PROPERTY_VALUE_NO)<<",";
    (*_csvOutput) << currentResults->entriesWithValue(PROPERTY_VALUE_UNKNOWN);
  }
  return currentResults;
}
示例#3
0
CEAnalysisResult CounterexampleAnalyzer::analyzeCounterexample(string counterexample, const EState* startState, 
								bool returnSpuriousLabel, bool resetAnalyzerData) {
  // OVERVIEW
  // (0) compare counterexample(CE) prefix with real trace
  // (1) initialize one hashset of seen EStates for each index of the CE cycle (bookkeeping)
  // (2) while (unknown whether or not CE is spurious)
  //   (2.1) compare one iteration of the CE's cycle part step-by-step with the original program
  //   (2.2) one step:  If (step is spurious) then determine the corresponding label in the approximated model and set "spuriousLabel". return "spurious"
  //                    Else if (step is not spurious and cycle found) then return "real"
  //                    Else store step in hashset (bookkeeping) and continue with the next step 
  // (3) return analysis result (including label for spurious transition's target, if existing)
  //cout << "DEBUG: counterexample: " << counterexample << endl;
  CEAnalysisResult result;
  result.analysisResult = CE_TYPE_UNKNOWN;
  PrefixAndCycle ceTrace = ceStringToInOutVals(counterexample);
  int stepsToSpuriousLabel = 0;
  if (returnSpuriousLabel) {
    //save abstract model in order to be able to traverse it later (in the case of a spurious CE)
    _analyzer->reduceToObservableBehavior();
    _analyzer->storeStgBackup();
  }
  // (0) compare the prefix with traces on the original program
  list<CeIoVal> cePrefix = ceTrace.first;
  //run prefix on original program
  EState* startEState;
  if (startState) {
    //workaround for the start state not being part of the STG without having transitions 
    startEState = const_cast<EState*>(startState);
  } else {
    startEState = const_cast<EState*>((_analyzer->getTransitionGraph())->getStartEState());
  }
  _analyzer->setAnalyzerToSolver8(startEState, resetAnalyzerData);
  setInputSequence(cePrefix);
  _analyzer->runSolver();
  _analyzer->reduceToObservableBehavior();
  TransitionGraph* traceGraph = _analyzer->getTransitionGraph(); 
  // compare
  const EState * compareFollowingHereNext = traceGraph->getStartEState();
  CEAnalysisStep currentResult = getSpuriousTransition(cePrefix, traceGraph, compareFollowingHereNext);
  if (currentResult.analysisResult == CE_TYPE_SPURIOUS) {
    // the number of steps to the spurious label will be updated at the end of this function
  } else if (currentResult.analysisResult == CE_TYPE_UNKNOWN) { // only continue if prefix was not spurious
    list<CeIoVal> ceCycle = ceTrace.second;
    stepsToSpuriousLabel = cePrefix.size();
    // (1) initialize one hashset per index in the cyclic part of the CE (bookkeeping)
    StateSets* statesPerCycleIndex = new StateSets(); 
    for (unsigned int i = 0; i < ceCycle.size(); i++) {
      boost::unordered_set<const EState*> newSet;
      statesPerCycleIndex->push_back(newSet);
    }
    //(2) while (unknown whether or not CE is spurious)
    while (currentResult.analysisResult == CE_TYPE_UNKNOWN) {
      if (currentResult.continueTracingOriginal) {
        //run one cycle iteration on the original program
        EState* continueTracingFromHere = const_cast<EState*>(_analyzer->getEstateBeforeMissingInput());
        assert(continueTracingFromHere);
        setInputSequence(ceCycle);
        _analyzer->continueAnalysisFrom(continueTracingFromHere);
        _analyzer->reduceToObservableBehavior();
        traceGraph = _analyzer->getTransitionGraph();
      }
      //compare
      compareFollowingHereNext = currentResult.mostRecentStateRealTrace;
      currentResult = getSpuriousTransition(ceCycle, traceGraph, compareFollowingHereNext, statesPerCycleIndex);
      if (currentResult.analysisResult == CE_TYPE_UNKNOWN) {
        stepsToSpuriousLabel += ceCycle.size();
      }
    }
    delete statesPerCycleIndex;
    statesPerCycleIndex = NULL;
  }
  // (3) process and return result
  if (returnSpuriousLabel) {
    _analyzer->swapStgWithBackup();
  }
  if (currentResult.analysisResult == CE_TYPE_SPURIOUS) { 
    result.analysisResult = CE_TYPE_SPURIOUS;
    //determine final number of steps to first observable spurious transition
    //   special case for an output followed by a failing assertion in the original program 
    //   (The approximated model trace does not contain the assertion. Refinement will use the assertion's guarding condition.)
    if (currentResult.assertionCausingSpuriousInput) {
      if (returnSpuriousLabel) {
        result.spuriousTargetLabel = currentResult.failingAssertionInOriginal;
      }
    } else {
      stepsToSpuriousLabel += currentResult.spuriousIndexInCurrentPart;
      if (returnSpuriousLabel) {
        result.spuriousTargetLabel = getFirstObservableSpuriousLabel(_analyzer->getTransitionGraph(), ceTrace, stepsToSpuriousLabel);
      }
    }
  } else if (currentResult.analysisResult == CE_TYPE_REAL) {
    result.analysisResult = CE_TYPE_REAL;
  } else {
    cout << "ERROR: counterexample should be either spurious or real." << endl;
    assert(0);
  }
  return result;
}