diff --git a/src/main/java/avatartranslator/AvatarExpressionSolver.java b/src/main/java/avatartranslator/AvatarExpressionSolver.java index 5f192339466d4a728f4123e8b746b0da66e71660..b9773bf0fa8281cf7eadfb1effe195b71d4aa6fd 100644 --- a/src/main/java/avatartranslator/AvatarExpressionSolver.java +++ b/src/main/java/avatartranslator/AvatarExpressionSolver.java @@ -760,6 +760,17 @@ public class AvatarExpressionSolver { } } + public void setBlockIndex(AvatarSpecification spec) { + if (isLeaf) { + if (isImmediateValue == IMMEDIATE_NO) { + leaf.setBlockIndex(spec); + } + } else { + left.setBlockIndex(spec); + right.setBlockIndex(spec); + } + } + public static boolean containsElementAttribute(AvatarElement ae) { if (attributesMap != null) { diff --git a/src/main/java/avatartranslator/AvatarSpecification.java b/src/main/java/avatartranslator/AvatarSpecification.java index 44b5770b6cce5659b93ede234bed61997af1e2d1..d6b46e511c0b9d5916750367125358a3212057db 100644 --- a/src/main/java/avatartranslator/AvatarSpecification.java +++ b/src/main/java/avatartranslator/AvatarSpecification.java @@ -452,6 +452,28 @@ public class AvatarSpecification extends AvatarElement { block.setAttributeOptRatio(attributeOptRatio); } } + + public List<ArrayList<AvatarTransition>> checkStaticInternalLoops() { + List<ArrayList<AvatarTransition>> loops = new ArrayList<ArrayList<AvatarTransition>>(); + + //collect all the possible loops which do not contain signal communication + for(AvatarBlock block: blocks) { + block.getStateMachine().checkStaticInternalLoops(loops); + } + + return loops; + +// //check presence of unguarded loops +// for (ArrayList<AvatarTransition> trace : loops) { +// boolean guarded = false; +// for (AvatarTransition at : trace) { +// guarded |= at.isGuarded(); +// } +// if (!guarded) { +// //no transition has a guard check --> reachability of first state +// } +// } + } // // private void renameTimers() { // // Check whether timers have the same name in different blocks diff --git a/src/main/java/avatartranslator/AvatarStateMachine.java b/src/main/java/avatartranslator/AvatarStateMachine.java index 55b610d6e49fe13db9305fdcbcd1855fc30dccae..4c8b6310f3511e865600638e70356387295d779e 100644 --- a/src/main/java/avatartranslator/AvatarStateMachine.java +++ b/src/main/java/avatartranslator/AvatarStateMachine.java @@ -43,6 +43,7 @@ import myutil.TraceManager; import java.util.*; + /** * Class AvatarStateMachine * State machine, with composite states @@ -1866,5 +1867,54 @@ public class AvatarStateMachine extends AvatarElement { return invalids; } + + + public boolean checkStaticInternalLoops(List<ArrayList<AvatarTransition>> loops) { + if (allStates == null) { + return false; + } + + List<AvatarTransition> trace = new ArrayList<AvatarTransition>(); + Set<AvatarStateMachineElement> visited= new HashSet<AvatarStateMachineElement>(); + + for (AvatarStateElement state : allStates) { + checkStaticInternalLoopsRec(state, state, trace, visited, loops, 0); + visited.add(state); //avoid cycles permutations + } + return true; + } + + + public void checkStaticInternalLoopsRec(AvatarStateMachineElement node, AvatarStateMachineElement arrival, List<AvatarTransition> trace, Set<AvatarStateMachineElement> visited, List<ArrayList<AvatarTransition>> loops, int depth) { + if (visited.contains(node)) { + if (node == arrival) { + //valid loop, copy trace to loops + loops.add(new ArrayList<AvatarTransition>(trace)); + return; + } else { + //not valid loop + return; + } + } else if (node.nexts == null){ + return; + } + + visited.add(node); + for (AvatarStateMachineElement next : node.nexts) { + if (next instanceof AvatarTransition) { + AvatarTransition at = (AvatarTransition) next; + //the state machine should alternate states and transitions + if (!(at.getNext(0) instanceof AvatarActionOnSignal)) { + //Choose internal action paths + trace.add(depth, at); + checkStaticInternalLoopsRec(at.getNext(0), arrival, trace, visited, loops, depth + 1); + trace.remove(depth); + } + } + } + visited.remove(node); + + return; + } } diff --git a/src/main/java/avatartranslator/modelchecker/AvatarModelChecker.java b/src/main/java/avatartranslator/modelchecker/AvatarModelChecker.java index f34898efe4aed9c0853b9f6aeb9276eae78302ff..1d79f836e48ece499e2fde613a8593407e923bc1 100644 --- a/src/main/java/avatartranslator/modelchecker/AvatarModelChecker.java +++ b/src/main/java/avatartranslator/modelchecker/AvatarModelChecker.java @@ -112,6 +112,10 @@ public class AvatarModelChecker implements Runnable, myutil.Graph { private boolean studyReinit; private SpecificationReinit initState; + // Internal-action loop + private boolean studyActionLoop; + private ArrayList<SpecificationActionLoop> actionLoops; + //Debug counterexample private boolean counterexample; private boolean counterTraceText; @@ -376,6 +380,29 @@ public class AvatarModelChecker implements Runnable, myutil.Graph { return false; } + public void setInternalActionLoopAnalysis(boolean studyActionLoop) { + this.studyActionLoop = studyActionLoop; + } + + + public ArrayList<SpecificationActionLoop> getInternalActionLoops() { + if (studyActionLoop) { + return actionLoops; + } + return null; + } + + public boolean getInternalActionLoopsResult() { + if (studyActionLoop && actionLoops != null) { + for (SpecificationActionLoop sal : actionLoops) { + if (sal.getResult()) { + return false; + } + } + } + return true; + } + public void setCounterExampleTrace(boolean counterTraceText, boolean counterTraceAUT) { this.counterexample = counterTraceText || counterTraceAUT; this.counterTraceText = counterTraceText; @@ -424,7 +451,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph { }*/ public boolean startModelCheckingProperties() { - boolean studyS, studyL, studyR, studyRI, genRG, genTrace; + boolean studyS, studyL, studyR, studyRI, studyAL, genRG, genTrace; boolean emptyTr, ignoreConcurrence; long deadlocks = 0; @@ -449,6 +476,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph { studyL = studyLiveness; studyS = studySafety; studyRI = studyReinit; + studyAL = studyActionLoop; genRG = computeRG; genTrace = counterexample; verboseInfo = false; @@ -460,6 +488,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph { studyLiveness = false; studyReachability = false; studyReinit = false; + studyActionLoop = false; counterexample = false; @@ -538,6 +567,32 @@ public class AvatarModelChecker implements Runnable, myutil.Graph { studyReinit = false; } + if (studyAL) { + studySafety = true; + ignoreConcurrenceBetweenInternalActions = true; + List<ArrayList<AvatarTransition>> internalLoops = spec.checkStaticInternalLoops(); + if (!internalLoops.isEmpty()) { + actionLoops = new ArrayList<SpecificationActionLoop>(); + for (ArrayList<AvatarTransition> loop : internalLoops) { + SpecificationActionLoop sap = new SpecificationActionLoop(loop, spec); + if (!sap.hasError()) { + actionLoops.add(sap); + safety = sap.getReachability(); + startModelChecking(nbOfThreads); + resetModelChecking(); + if (sap.hasProperty()) { + safety = sap.getProperty(); + startModelChecking(nbOfThreads); + resetModelChecking(); + } + sap.setResult(); + } + } + } + ignoreConcurrenceBetweenInternalActions = ignoreConcurrence; + studySafety = false; + } + if (checkNoDeadlocks) { //If a complete study with reachability graph generation has been executed, //there is no need to study deadlocks again @@ -581,6 +636,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph { studySafety = studyS; studyReachability = studyR; studyReinit = studyRI; + studyActionLoop = studyAL; TraceManager.addDev("Model checking done"); return true; @@ -773,10 +829,12 @@ public class AvatarModelChecker implements Runnable, myutil.Graph { spec.sortAttributes(); spec.setAttributeOptRatio(compressionFactor); spec.generateAllExpressionSolvers(); + prepareTransitions(); prepareBlocks(); - + + spec.checkStaticInternalLoops(); nbOfThreads = Runtime.getRuntime().availableProcessors(); TraceManager.addDev("Starting the model checking with " + nbOfThreads + " threads"); diff --git a/src/main/java/avatartranslator/modelchecker/SafetyProperty.java b/src/main/java/avatartranslator/modelchecker/SafetyProperty.java index 4f39eff0791002e37370ef2ef27c8ef52a5a3751..1c35663399c1879aadbe4c1266c7a6bcb79f6ece 100644 --- a/src/main/java/avatartranslator/modelchecker/SafetyProperty.java +++ b/src/main/java/avatartranslator/modelchecker/SafetyProperty.java @@ -103,27 +103,8 @@ public class SafetyProperty { String tmpP = rawProperty.trim(); String p; - errorOnProperty = NO_ERROR; - - if (tmpP.startsWith("A[]")) { - safetyType = ALLTRACES_ALLSTATES; - result = true; - } else if (tmpP.startsWith("A<>")) { - safetyType = ALLTRACES_ONESTATE; - result = true; - } else if (tmpP.startsWith("E[]")) { - safetyType = ONETRACE_ALLSTATES; - result = false; - } else if (tmpP.startsWith("E<>")) { - safetyType = ONETRACE_ONESTATE; - result = false; - } else if (tmpP.contains("-->")){ - safetyType = LEADS_TO; - result = true; - } else { - errorOnProperty = BAD_SAFETY_TYPE; - result = false; - return false; + if (!initType(tmpP)) { + return false; } if (safetyType != LEADS_TO) { @@ -138,6 +119,28 @@ public class SafetyProperty { return (errorOnProperty == NO_ERROR); } + //for local variables inside a block + public boolean analyzeProperty(AvatarBlock block, AvatarSpecification _spec) { + String tmpP = rawProperty.trim(); + String p; + + if (!initType(tmpP)) { + return false; + } + + if (safetyType != LEADS_TO) { + p = tmpP.substring(3, tmpP.length()).trim(); + initSafetyTrace(block, _spec, p); + } else { + p = tmpP; + initSafetyLeads(block, _spec, p); + } + + + return (errorOnProperty == NO_ERROR); + } + + public void initLead() { safetySolver = safetySolverLead; safetyType = ALLTRACES_ONESTATE; @@ -238,6 +241,32 @@ public class SafetyProperty { return name; } + private boolean initType(String tmpP) { + errorOnProperty = NO_ERROR; + + if (tmpP.startsWith("A[]")) { + safetyType = ALLTRACES_ALLSTATES; + result = true; + } else if (tmpP.startsWith("A<>")) { + safetyType = ALLTRACES_ONESTATE; + result = true; + } else if (tmpP.startsWith("E[]")) { + safetyType = ONETRACE_ALLSTATES; + result = false; + } else if (tmpP.startsWith("E<>")) { + safetyType = ONETRACE_ONESTATE; + result = false; + } else if (tmpP.contains("-->")){ + safetyType = LEADS_TO; + result = true; + } else { + errorOnProperty = BAD_SAFETY_TYPE; + result = false; + return false; + } + return true; + } + private boolean initSafetyTrace(AvatarSpecification _spec, String p) { safetySolver = new AvatarExpressionSolver(p); @@ -256,6 +285,25 @@ public class SafetyProperty { return exprRet; } + private boolean initSafetyTrace(AvatarBlock block, AvatarSpecification _spec, String p) { + safetySolver = new AvatarExpressionSolver(p); + boolean exprRet = safetySolver.buildExpression(block); + + if (exprRet == false) { + errorOnProperty = BAD_PROPERTY_STRUCTURE; + } + + safetySolver.setBlockIndex(_spec); + + if (safetySolver.hasState()) { + propertyType = BLOCK_STATE; + } else { + propertyType = BOOL_EXPR; + } + + return exprRet; + } + private boolean initSafetyLeads(AvatarSpecification _spec, String p) { String[] pFields; @@ -301,5 +349,54 @@ public class SafetyProperty { return true; } + + private boolean initSafetyLeads(AvatarBlock block, AvatarSpecification _spec, String p) { + String[] pFields; + String pp, pq; + boolean exprRet; + + pFields = p.split("-->"); + if (pFields.length != 2) { + errorOnProperty = BAD_PROPERTY_STRUCTURE; + return false; + } + + pp = pFields[0].trim(); + pq = pFields[1].trim(); + + safetySolver = new AvatarExpressionSolver(pp); + exprRet = safetySolver.buildExpression(block); + + if (exprRet == false) { + errorOnProperty = BAD_PROPERTY_STRUCTURE; + return false; + } + + safetySolver.setBlockIndex(_spec); + + safetySolverLead = new AvatarExpressionSolver(pq); + exprRet = safetySolverLead.buildExpression(block); + + if (exprRet == false) { + errorOnProperty = BAD_PROPERTY_STRUCTURE; + return false; + } + + safetySolverLead.setBlockIndex(_spec); + + if (safetySolver.hasState()) { + propertyType = BLOCK_STATE; + } else { + propertyType = BOOL_EXPR; + } + + if (safetySolverLead.hasState()) { + leadType = BLOCK_STATE; + } else { + leadType = BOOL_EXPR; + } + + return true; + } } diff --git a/src/main/java/avatartranslator/modelchecker/SpecificationActionLoop.java b/src/main/java/avatartranslator/modelchecker/SpecificationActionLoop.java new file mode 100644 index 0000000000000000000000000000000000000000..c5da755352834877a81411d50a4551fb75c5f9ed --- /dev/null +++ b/src/main/java/avatartranslator/modelchecker/SpecificationActionLoop.java @@ -0,0 +1,90 @@ +package avatartranslator.modelchecker; + +import java.util.List; + +import avatartranslator.AvatarBlock; +import avatartranslator.AvatarSpecification; +import avatartranslator.AvatarTransition; + +public class SpecificationActionLoop { + private List<AvatarTransition> path; + private SafetyProperty reachability; + private SafetyProperty property; + private boolean hasProperty; + private boolean result; + private boolean error; + + public SpecificationActionLoop(List<AvatarTransition> path, AvatarSpecification spec) { + this.path = path; + this.error = false; + init(spec); + } + + private void init(AvatarSpecification spec) { + //check presence of unguarded loops + boolean guarded = false; + StringBuilder condition = new StringBuilder("!("); + for (AvatarTransition at : path) { + if (at.isGuarded()) { + String guard = at.getGuard().toString().replaceAll("\\[", "").trim().replaceAll("\\]", ""); + if (guarded) { + condition.append(" && " + guard); + } else { + condition.append(guard); + } + guarded = true; + } + } + condition.append(")"); + AvatarTransition at = path.get(path.size() - 1); + //no transition has a guard check --> reachability of first state + reachability = new SafetyProperty("E<> " + at.getBlock().getName() + "." + at.getNext(0).getName()); + reachability.analyzeProperty(spec); + error = reachability.hasError(); + hasProperty = false; + if (guarded) { + hasProperty = true; + property = new SafetyProperty("E<> " + at.getNext(0).getName() + " && " + condition.toString()); + property.analyzeProperty((AvatarBlock) at.getBlock(), spec); + error |= property.hasError(); + } + } + + public boolean hasError() { + return error; + } + + public boolean hasProperty() { + return hasProperty; + } + + public SafetyProperty getReachability() { + return reachability; + } + + public SafetyProperty getProperty() { + return property; + } + + public void setResult() { + result = reachability.result; + if (hasProperty) { + result &= !property.result; + } + } + + public boolean getResult() { + return result; + } + + public String toString() { + StringBuilder s = new StringBuilder(); + + s.append("In block " + path.get(path.size() - 1).getBlock().getName() + " : " + path.get(path.size() - 1).getNext(0).getName()); + for (AvatarTransition at : path) { + s.append(" --> " + at.getNext(0).getName()); + } + + return s.toString(); + } +} diff --git a/src/main/java/ui/window/JDialogAvatarModelChecker.java b/src/main/java/ui/window/JDialogAvatarModelChecker.java index 1592e9e3178a095b2308ee53895d5d3d482a9124..44de8fdafa01b846598cefa6fe00677c3c8b2c7e 100644 --- a/src/main/java/ui/window/JDialogAvatarModelChecker.java +++ b/src/main/java/ui/window/JDialogAvatarModelChecker.java @@ -44,6 +44,7 @@ import avatartranslator.AvatarStateMachineElement; import avatartranslator.modelchecker.AvatarModelChecker; import avatartranslator.modelchecker.CounterexampleQueryReport; import avatartranslator.modelchecker.SafetyProperty; +import avatartranslator.modelchecker.SpecificationActionLoop; import avatartranslator.modelchecker.SpecificationReachability; import avatartranslator.modelchecker.SpecificationPropertyPhase; import myutil.*; @@ -105,6 +106,7 @@ public class JDialogAvatarModelChecker extends javax.swing.JFrame implements Act protected static boolean safetySelected = false; protected static boolean checkNoDeadSelected = false; protected static boolean checkReinitSelected = false; + protected static boolean checkActionLoopSelected = false; protected static boolean limitStatesSelected = false; protected static boolean generateCountertraceSelected = false; protected static boolean generateCountertraceAUTSelected = false; @@ -159,6 +161,7 @@ public class JDialogAvatarModelChecker extends javax.swing.JFrame implements Act protected JTextField timeLimitField; protected JCheckBox noDeadlocks; protected JCheckBox reinit; + protected JCheckBox actionLoop; protected JCheckBox safety; protected JCheckBox countertrace; protected JCheckBox countertraceAUT; @@ -321,10 +324,15 @@ public class JDialogAvatarModelChecker extends javax.swing.JFrame implements Act // Reinit - cbasic.gridwidth = GridBagConstraints.REMAINDER; reinit = new JCheckBox("Reinitialization?", checkReinitSelected); reinit.addActionListener(this); jpbasic.add(reinit, cbasic); + + // Internal action loop + cbasic.gridwidth = GridBagConstraints.REMAINDER; + actionLoop = new JCheckBox("No internal action loops? (test)", checkActionLoopSelected); + actionLoop.addActionListener(this); + jpbasic.add(actionLoop, cbasic); // Reachability @@ -735,6 +743,7 @@ public class JDialogAvatarModelChecker extends javax.swing.JFrame implements Act amc.setIgnoreInternalStates(ignoreInternalStatesSelected); amc.setCheckNoDeadlocks(checkNoDeadSelected); amc.setReinitAnalysis(checkReinitSelected); + amc.setInternalActionLoopAnalysis(checkActionLoopSelected); amc.setCounterExampleTrace(generateCountertraceSelected, generateCountertraceAUTSelected); // Reachability @@ -838,7 +847,7 @@ public class JDialogAvatarModelChecker extends javax.swing.JFrame implements Act // Starting model checking testGo(); - if (livenessSelected == LIVENESS_NONE && safetySelected == false && checkNoDeadSelected == false) { + if (livenessSelected == LIVENESS_NONE && safetySelected == false && checkNoDeadSelected == false && checkActionLoopSelected == false) { amc.startModelChecking(); } else { amc.startModelCheckingProperties(); @@ -869,6 +878,21 @@ public class JDialogAvatarModelChecker extends javax.swing.JFrame implements Act if (checkReinitSelected) { jta.append("\nReinitialization?\n" + "-> " + amc.reinitToString() + "\n"); } + + if (checkActionLoopSelected) { + boolean result = amc.getInternalActionLoopsResult(); + jta.append("\nNo internal action loops?\n" + "-> " + result + "\n"); + + if (!result) { + ArrayList<SpecificationActionLoop> al = amc.getInternalActionLoops(); + jta.append("Internal action loops:\n"); + for (SpecificationActionLoop sal : al) { + if (sal.getResult()) { + jta.append(sal.toString() + "\n"); + } + } + } + } if ((reachabilitySelected == REACHABILITY_SELECTED) || (reachabilitySelected == REACHABILITY_ALL)) { jta.append("\nReachabilities found:\n"); @@ -1090,6 +1114,7 @@ public class JDialogAvatarModelChecker extends javax.swing.JFrame implements Act ignoreInternalStatesSelected = ignoreInternalStates.isSelected(); checkNoDeadSelected = noDeadlocks.isSelected(); checkReinitSelected = reinit.isSelected(); + checkActionLoopSelected = actionLoop.isSelected(); if (noReachability.isSelected()) { reachabilitySelected = REACHABILITY_NONE; @@ -1129,7 +1154,7 @@ public class JDialogAvatarModelChecker extends javax.swing.JFrame implements Act switch (mode) { case NOT_STARTED: - if ((reachabilitySelected == REACHABILITY_SELECTED) || (reachabilitySelected == REACHABILITY_ALL) || (livenessSelected == LIVENESS_SELECTED) || (livenessSelected == LIVENESS_ALL) || checkNoDeadSelected || checkReinitSelected || graphSelected || graphSelectedDot) { + if ((reachabilitySelected == REACHABILITY_SELECTED) || (reachabilitySelected == REACHABILITY_ALL) || (livenessSelected == LIVENESS_SELECTED) || (livenessSelected == LIVENESS_ALL) || checkNoDeadSelected || checkReinitSelected || checkActionLoopSelected || graphSelected || graphSelectedDot) { start.setEnabled(true); } else { if (safetySelected) {