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; }
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 }
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. }