Пример #1
0
// MS: we definitely need to cache all the results or use a proper graph structure
TransitionGraph::TransitionPtrSet TransitionGraph::outEdges(const EState* estate) {
    ROSE_ASSERT(estate);
    if(getModeLTLDriven()) {
        ROSE_ASSERT(_analyzer);
        if(_outEdges[estate].size()==0) {

            ROSE_ASSERT(_analyzer);
            Analyzer::SubSolverResultType subSolverResult=_analyzer->subSolver(estate);
            EStateWorkList& deferedWorkList=subSolverResult.first;
            EStateSet& existingEStateSet=subSolverResult.second;
            EStatePtrSet succNodes;
            for(EStateWorkList::iterator i=deferedWorkList.begin(); i!=deferedWorkList.end(); ++i) {
                succNodes.insert(*i);
            }
            for(EStateSet::iterator i=existingEStateSet.begin(); i!=existingEStateSet.end(); ++i) {
                succNodes.insert(*i);
            }
            //cout<<"DEBUG: succ:"<<deferedWorkList.size()<<","<<existingEStateSet.size()<<":"<<succNodes.size()<<endl;
            for(EStatePtrSet::iterator j=succNodes.begin(); j!=succNodes.end(); ++j) {
                Edge newEdge(estate->label(),EDGE_PATH,(*j)->label());
                Transition t(estate,newEdge,*j);
                add(t);
            }
        }
        //if(_outEdges[estate].size()>0) cout<<"DEBUG: #out-edges="<<_outEdges[estate].size()<<endl;
    }
    return _outEdges[estate];
}
Пример #2
0
EStatePtrSet TransitionGraph::succ(const EState* estate) {
    EStatePtrSet succNodes;
    TransitionPtrSet tset=outEdges(estate);
    for(TransitionPtrSet::iterator i=tset.begin(); i!=tset.end(); ++i) {
        succNodes.insert((*i)->target);
    }
    return succNodes;
}
Пример #3
0
EStatePtrSet TransitionGraph::pred(const EState* estate) {
    EStatePtrSet predNodes;
    TransitionPtrSet tset=inEdges(estate);
    for(TransitionPtrSet::iterator i=tset.begin(); i!=tset.end(); ++i) {
        predNodes.insert((*i)->source);
    }
    return predNodes;
}
Пример #4
0
vector<const EState*> CounterexampleAnalyzer::sortAbstractInputStates(vector<const EState*> v, EStatePtrSet abstractInputStates) {
  for (EStatePtrSet::iterator i=abstractInputStates.begin(); i!=abstractInputStates.end(); ++i) {
    PState* pstate = const_cast<PState*>( (*i)->pstate() ); 
    int inVal = (*pstate)[_analyzer->globalVarIdByName("input")].getValue().getIntValue();
    v[inVal - 1] = (*i);
  }
  return v;
}
Пример #5
0
CEAnalysisStep CounterexampleAnalyzer::getSpuriousTransition(list<CeIoVal> partOfCeTrace, TransitionGraph* originalTraceGraph, 
                                           const EState* compareFollowingHere, StateSets* statesPerCycleIndex) {
  assert(compareFollowingHere);
  const EState* currentState = compareFollowingHere;
  bool matchingState;
  CeIoVal realBehavior;
  int index = 1;
  CEAnalysisStep result;
  result.assertionCausingSpuriousInput = false;
  StateSets::iterator cycleIndexStates;
  if (statesPerCycleIndex) { //bookkeeping for analyzing the cyclic part of the counterexample
    cycleIndexStates = statesPerCycleIndex->begin();
  }
  // iterate over part of the counterexample and compare its behavior step-by-step against the original program's trace
  for (list<CeIoVal>::iterator i = partOfCeTrace.begin(); i != partOfCeTrace.end(); i++) {
    matchingState = false;
    EStatePtrSet successors = originalTraceGraph->succ(currentState);
    // the trace graph should always contain enough states for the comparision
    assert(successors.size()>0);
    // retrieve the correct state at branches in the original program (branches may exist due to inherent loops)
    // note: assuming deterministic input/output programs
    for (EStatePtrSet::iterator succ = successors.begin(); succ != successors.end(); succ++) {
      realBehavior = eStateToCeIoVal(*succ);
      //cout << "DEBUG: realBehavior: " << ceIoValToString(realBehavior) << "   ceTraceItem: " << ceIoValToString(*i) << endl;
      if (realBehavior == (*i)) {  //no spurious behavior when following this path, so continue here
        currentState = (*succ);
        index++;
        matchingState = true;
        if (statesPerCycleIndex) {
          //check if the real state has already been seen
          pair<boost::unordered_set<const EState*>::iterator, bool> notYetSeen = cycleIndexStates->insert(currentState);
          if (notYetSeen.second == false) {
            //state encountered twice at the some counterexample index. cycle found --> real counterexample
            result.analysisResult = CE_TYPE_REAL;
            return result;
          } else {
            cycleIndexStates++;
          }
        }
      // special case. output-> failing assertion occurs during execution of the original program, leading to a spurious input symbol.
      // RefinementConstraints should use the failing assertion's condition to add new constraints, trying to enforce the assertion to fail
      // and thus eliminating the spurious input symbol.
      } else if (realBehavior.second == IO_TYPE_ERROR && i->second == IO_TYPE_INPUT) {
        result.assertionCausingSpuriousInput = true;
        result.failingAssertionInOriginal = (*succ)->label();
      }
    }
    // if no matching transition was found, then the counterexample trace is spurious
    if (!matchingState) {
      result.analysisResult = CE_TYPE_SPURIOUS;
      result.spuriousIndexInCurrentPart = index;
      return result;
    }
  }
  IoType mostRecentOriginalIoType = (partOfCeTrace.rbegin())->second;
  determineAnalysisStepResult(result, mostRecentOriginalIoType, currentState, originalTraceGraph, index);
  return result; // partOfCeTrace could be successfully traversed on the originalTraceGraph
}
Пример #6
0
Label CounterexampleAnalyzer::getFirstObservableSpuriousLabel(TransitionGraph* analyzedModel, PrefixAndCycle counterexample, int numberOfSteps) {
  boost::unordered_set<const EState*>* currentDepth = new boost::unordered_set<const EState*>();
  boost::unordered_set<const EState*>* nextDepth = new boost::unordered_set<const EState*>();
  currentDepth->insert(analyzedModel->getStartEState());
  list<CeIoVal> prefix = counterexample.first;
  list<CeIoVal> cycle = counterexample.second;
  unsigned int prefixIndex = 0;
  unsigned int cycleIndex = 0;
  assert(prefix.size() > 0); // in case of an empty prefix, refine the initialization here
  list<CeIoVal>::iterator currentCeBehavior = prefix.begin();
  bool inCycle = false;
  // traverse all paths in the analyzed model that match with the counterexample's behavior
  for (int i = 0; i < numberOfSteps; i++) {
    //traverse one more level
    for (boost::unordered_set<const EState*>::iterator k = currentDepth->begin(); k != currentDepth->end(); k++) {
      EStatePtrSet successors = analyzedModel->succ(*k);
      // add those successor states to "nextDepth" that match the desired behavior
      for (EStatePtrSet::iterator m = successors.begin(); m != successors.end(); m++) {
        if (eStateToCeIoVal(*m) == *currentCeBehavior) {
          nextDepth->insert(*m);
        }
      }
    } 
    // swap currentDepth and nextDepth, then clear nextDepth
    boost::unordered_set<const EState*>* swapTemp = currentDepth;  
    currentDepth = nextDepth;
    nextDepth = swapTemp;
    nextDepth->clear();
    //update currentCeBehavior
    if (!inCycle) {
      if (prefixIndex < prefix.size() - 1) {
        currentCeBehavior++;
        prefixIndex++;
      } else { 
        currentCeBehavior = cycle.begin();
        inCycle = true;
      }
    } else {
      if (cycleIndex < cycle.size() - 1) {
        currentCeBehavior++;
        cycleIndex++;
      } else { 
        currentCeBehavior = cycle.begin();
        cycleIndex = 0;
      }
    }
  }
  // the counterexample itself comes from analyzing "anaylzedModel", there has to be at least one matching state
  assert (currentDepth->size() > 0);
  const EState* firstMatch = *(currentDepth->begin());
  delete currentDepth;
  delete nextDepth;
  assert(firstMatch);
  return firstMatch->label();
}
Пример #7
0
// author: Marc Jasper, 2015.
long TransitionGraph::numberOfObservableStates(bool includeIn, bool includeOut, bool includeErr) {
    long result = 0;
    EStatePtrSet allStates = estateSet();
    for (EStatePtrSet::iterator i=allStates.begin(); i!=allStates.end(); ++i) {
        if ((includeIn && (*i)->io.isStdInIO()) || (includeOut && (*i)->io.isStdOutIO())
                || (includeErr && ((*i)->io.isStdErrIO()||(*i)->io.isFailedAssertIO())) ) {
            result++;
        }
    }
    return result;
}
Пример #8
0
EStatePtrSet CounterexampleAnalyzer::addAllPrefixOutputStates(EStatePtrSet& startAndOuputStatesPrefix, TransitionGraph* model) {
  EStatePtrSet allEStates = model->estateSet();
  for (EStatePtrSet::iterator i=allEStates.begin(); i!=allEStates.end(); ++i) {
    //start state has already been added
    if ((*i)->io.isStdOutIO()) {
      if (! (*i)->isRersTopified(_analyzer->getVariableIdMapping())) {
        startAndOuputStatesPrefix.insert(*i);
      }
    }  
  }
  return startAndOuputStatesPrefix;
}
Пример #9
0
vector<bool> CounterexampleAnalyzer::hasFollowingInputStates(vector<bool> v, const EState*eState, TransitionGraph* model) {
  EStatePtrSet successors = model->succ(eState);
  for (EStatePtrSet::iterator k=successors.begin(); k!=successors.end(); ++k) {
    if ((*k)->io.isStdInIO()) {
      PState* pstate = const_cast<PState*>( (*k)->pstate() ); 
      int inVal = (*pstate)[_analyzer->globalVarIdByName("input")].getValue().getIntValue();
      v[inVal - 1] = true; 
    }else {
      cout << "ERROR: CounterexampleAnalyzer::cegarPrefixAnalysisForLtl: successor of prefix output (or start) state is not an input state." << endl;
      assert(0); 
    }
  }
  return v;
}
Пример #10
0
vector<const EState*> CounterexampleAnalyzer::getFollowingInputStates(vector<const EState*> v, const EState* startEState, TransitionGraph* model) {
  EStatePtrSet firstInputStates = model->succ(startEState);
  for (EStatePtrSet::iterator i=firstInputStates.begin(); i!=firstInputStates.end(); ++i) {
    if ((*i)->io.isStdInIO()) {
      PState* pstate = const_cast<PState*>( (*i)->pstate() ); 
      int inVal = (*pstate)[_analyzer->globalVarIdByName("input")].getValue().getIntValue();
      v[inVal - 1] = (*i);
    } else {
      cout << "ERROR: CounterexampleAnalyzer::cegarPrefixAnalysisForLtl: successor of initial model's start state is not an input state." << endl;
      assert(0); 
    }
  }
  return v;
}
Пример #11
0
list<pair<const EState*, int> > CounterexampleAnalyzer::removeTraceLeadingToErrorState(const EState* errorState, TransitionGraph* stg) {
  assert(errorState->io.isFailedAssertIO() || errorState->io.isStdErrIO() );
  list<pair<const EState*, int> > erroneousTransitions;
  PState* pstate = const_cast<PState*>( errorState->pstate() ); 
  int latestInputVal = (*pstate)[_analyzer->globalVarIdByName("input")].getValue().getIntValue();
  //eliminate the error state
  const EState* eliminateThisOne = errorState;
  EStatePtrSet preds = stg->pred(eliminateThisOne);
  assert (stg->succ(eliminateThisOne).size() == 0); // error state should have no successors.
  assert (preds.size() == 1); // error states are immediately removed after being discovered.
  const EState* currentState = *(preds.begin());
  stg->eliminateEState(eliminateThisOne);
  //in the case of new input->output->error behavior, delete the output too
  if (currentState->io.isStdOutIO()) {
    eliminateThisOne = currentState;
    preds = stg->pred(currentState);
    assert (preds.size() == 1);
    currentState = *(preds.begin());
    stg->eliminateEState(eliminateThisOne);
  }
  //also delete the input state right before
  assert (currentState->io.isStdInIO());
  eliminateThisOne = currentState;
  preds = stg->pred(currentState);
  // exclude all edges leading to the input state right before the error state for future tracing attempts
  // (due to the reduction to observable behavior, even a single execution trace can lead to multiple predecessors)
  for (EStatePtrSet::iterator i=preds.begin(); i!= preds.end(); ++i) {
    erroneousTransitions.push_back(pair<const EState*, int>(*i, latestInputVal));
  }
  stg->eliminateEState(eliminateThisOne);
  return erroneousTransitions;
}
Пример #12
0
pair<EStatePtrSet, EStatePtrSet> CounterexampleAnalyzer::getConcreteOutputAndAbstractInput(TransitionGraph* model) {
  EStatePtrSet allEStates=model->estateSet();
  EStatePtrSet concreteOutputStates;
  EStatePtrSet abstractInputStates;
  concreteOutputStates.insert(model->getStartEState());  // the start state has following input states just like output states do.
  for(EStatePtrSet::iterator i=allEStates.begin(); i!=allEStates.end(); ++i) {
    if ((*i)->isRersTopified(_analyzer->getVariableIdMapping())) {
      if ((*i)->io.isStdInIO()) {
        abstractInputStates.insert(*i);
      }
    } else {
      if ((*i)->io.isStdOutIO()) {
        concreteOutputStates.insert(*i);
      }
    } 
  }
  return pair<EStatePtrSet, EStatePtrSet> (concreteOutputStates, abstractInputStates);
}
Пример #13
0
void CounterexampleAnalyzer::determineAnalysisStepResult(CEAnalysisStep& result, IoType mostRecentOriginalIoType, 
                                                const EState* currentState, TransitionGraph* originalTraceGraph, int index) {
  result.analysisResult = CE_TYPE_UNKNOWN;
  result.mostRecentStateRealTrace = currentState;
  result.continueTracingOriginal = true;
  // set and return the result
  //  Takes care of the corner case that error states after the lastly assessed input symbol render
  //  further tracing of the original program's path impossible.
  if (mostRecentOriginalIoType == IO_TYPE_INPUT) {
    EStatePtrSet successors = originalTraceGraph->succ(currentState);
    assert(successors.size() == 1);  //(input-)determinism of the original program
    const EState * nextEState = *(successors.begin());
    assert(nextEState);
    if (nextEState->io.isStdErrIO() || nextEState->io.isFailedAssertIO()) {
      // (*) see below (the first symbol of the next partOfCeTrace has to be the spurious transition here)
      result.analysisResult = CE_TYPE_SPURIOUS;
      result.spuriousIndexInCurrentPart = index;
    } else if (nextEState->io.isStdOutIO()) {
      successors = originalTraceGraph->succ(nextEState);
      for (EStatePtrSet::iterator i = successors.begin(); i != successors.end(); ++i) {
        const EState* nextEState = *i;
        if (nextEState->io.isStdErrIO() || nextEState->io.isFailedAssertIO()) {
          // (*) see below
          result.continueTracingOriginal = false;
        }
      }
    }
  } else if (mostRecentOriginalIoType == IO_TYPE_OUTPUT) {
    EStatePtrSet successors = originalTraceGraph->succ(currentState);
    for (EStatePtrSet::iterator i = successors.begin(); i != successors.end(); ++i) {
      const EState* nextEState = *i;
      if (nextEState->io.isStdErrIO() || nextEState->io.isFailedAssertIO()) {
        // (*) see below
        result.continueTracingOriginal = false;
      }
    }
  }
  // (*) there is a not yet seen error state. Because every state except from successors of the last input state of the trace have
  //     been looked at already, the cycle part of the CE contains no input state before the error state. RERS programs need to
  //     contain at least one input symbol in the cycle part of a counterexample --> this counterexample is spurious.
}
Пример #14
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;
}