All Downloads are FREE. Search and download functionalities are using the official Maven repository.

il.ac.bgu.cs.bp.bpjs.analysis.DfsBProgramVerifier Maven / Gradle / Ivy

/*
 * The MIT License
 *
 * Copyright 2017 michael.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package il.ac.bgu.cs.bp.bpjs.analysis;

import il.ac.bgu.cs.bp.bpjs.BPjs;
import il.ac.bgu.cs.bp.bpjs.analysis.violations.JsErrorViolation;
import il.ac.bgu.cs.bp.bpjs.analysis.violations.Violation;
import il.ac.bgu.cs.bp.bpjs.exceptions.BPjsRuntimeException;
import il.ac.bgu.cs.bp.bpjs.execution.jsproxy.BpLog;
import java.util.ArrayList;
import java.util.List;

import il.ac.bgu.cs.bp.bpjs.model.BProgram;
import il.ac.bgu.cs.bp.bpjs.model.BEvent;
import il.ac.bgu.cs.bp.bpjs.model.BProgramSyncSnapshot;
import java.util.HashSet;

import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.stream.Collectors.toSet;

/**
 * Takes a {@link BProgram}, and verifies that it does not run into false
 * assertions or deadlock, given all possible event selections.
 * Take care to use the appropriate {@link VisitedStateStore} for the
 * {@link BProgram} being verified.
 * 

* States are scanned using a DFS. * * @author michael */ public class DfsBProgramVerifier { private static final AtomicInteger INSTANCE_COUNTER = new AtomicInteger(); public final static long DEFAULT_MAX_TRACE = 1000; /** * Default number of iterations between invoking the progress listeners. */ public final static long DEFAULT_ITERATION_COUNT_GAP = 1000; /** * A listener to the progress of the DFS state scanning. */ public static interface ProgressListener { /** * Verifier {@code vfr} started a verification process. * @param vfr the verifier */ void started(DfsBProgramVerifier vfr); /** * A periodical call to update progress. * @param count count of iterations. * @param statesHit count of states found. * @param vfr the verifier * @see DfsBProgramVerifier#setIterationCountGap(long) */ void iterationCount(long count, long statesHit, DfsBProgramVerifier vfr); /** * Verifier {@code vfr} hit the max trace length, and now pops its * iteration stack. * @param aTrace The trace the verifier examined when it hit the length limit. * @param vfr The verifier. * @see DfsBProgramVerifier#setMaxTraceLength(long) */ void maxTraceLengthHit(ExecutionTrace aTrace, DfsBProgramVerifier vfr); /** * Verifier {@code vfr} reports a found violation. It is up to the listener * to decide whether to continue the verification process and find more * violations, or to terminate and return the current violation to the * caller. * * @param aViolation the violation found * @param vfr the verifier that found the violation * @return {@code true} for the verifier to continue the search, {@code false} otherwise. */ boolean violationFound( Violation aViolation, DfsBProgramVerifier vfr ); /** * The verifier {@code vfr} has finished the verification process. * @param vfr the verifier that found the violation */ void done(DfsBProgramVerifier vfr); } private static class ViolatingPathFoundException extends Exception { final Violation v; public ViolatingPathFoundException(Violation v) { this.v = v; } } /** * A "null object" progress listener instance. Stops verification at the * first violation found. Otherwise does nothing. */ private static final ProgressListener NULL_PROGRESS_LISTENER = new ProgressListener() { @Override public void started(DfsBProgramVerifier vfr) {} @Override public void iterationCount(long count, long statesHit, DfsBProgramVerifier vfr) {} @Override public void maxTraceLengthHit(ExecutionTrace aTrace, DfsBProgramVerifier vfr) {} @Override public void done(DfsBProgramVerifier vfr) {} @Override public boolean violationFound(Violation aViolation, DfsBProgramVerifier vfr) { return false; } }; private long visitedEdgeCount; private VisitedStateStore visited = new BProgramSnapshotVisitedStateStore(); private long maxTraceLength = DEFAULT_MAX_TRACE; private final List currentPath = new ArrayList<>(); private ProgressListener listener; private long iterationCountGap = DEFAULT_ITERATION_COUNT_GAP; private BProgram currentBProgram; private boolean debugMode = false; private final Set inspections = new HashSet<>(); private ArrayExecutionTrace trace; public DfsBProgramVerifier(BProgram aBp) { currentBProgram = aBp; trace = new ArrayExecutionTrace(currentBProgram); } public DfsBProgramVerifier() { } public VerificationResult verify(BProgram aBp) throws Exception { if ( listener == null ) { listener = NULL_PROGRESS_LISTENER; } currentBProgram = aBp; visitedEdgeCount = 0; currentPath.clear(); visited.clear(); trace = new ArrayExecutionTrace(currentBProgram); // in case no verifications were specified, use the defauls set. if ( inspections.isEmpty() ) { inspections.addAll( ExecutionTraceInspections.DEFAULT_SET ); } ExecutorService execSvc = BPjs.getExecutorServiceMaker().makeWithName("DfsBProgramRunner-" + INSTANCE_COUNTER.incrementAndGet()); BpLog.LogLevel prevLevel = currentBProgram.getLogLevel(); if ( ! BPjs.isLogDuringVerification() ) { currentBProgram.setLogLevel(BpLog.LogLevel.Off); } long start = System.currentTimeMillis(); listener.started(this); try { Violation vio = dfsUsingStack(new DfsTraversalNode(currentBProgram, currentBProgram.setup().start(execSvc, currentBProgram.getStorageModificationStrategy()), null), execSvc ); long end = System.currentTimeMillis(); return new VerificationResult(vio, end - start, visited.getVisitedStateCount(), visitedEdgeCount); } finally { listener.done(this); execSvc.shutdown(); if ( ! BPjs.isLogDuringVerification() ) { currentBProgram.setLogLevel(prevLevel); } } } protected Violation dfsUsingStack(DfsTraversalNode aStartNode, ExecutorService execSvc) throws Exception { long iterationCount = 0; push(aStartNode); Violation v = inspectCurrentTrace(); if ( v != null ) return v; while (!isPathEmpty()) { iterationCount++; if (debugMode) { printStatus(iterationCount, currentPath); } DfsTraversalNode curNode = peek(); if (pathLength() == maxTraceLength) { // fold stack; listener.maxTraceLengthHit(trace, this); pop(); } else { try { DfsTraversalNode nextNode = getUnvisitedNextNode(curNode, execSvc); if (nextNode == null) { // fold stack, retry next iteration; if (isDebugMode()) { System.out.println("-pop!-"); } DfsTraversalNode p = pop(); if ( p.getEventIterator().hasNext() ) { throw new IllegalStateException("Still having some events to traverse: " + p.getEventIterator().next() ); } } else { // go deeper if (isDebugMode()) { System.out.println("-visiting: " + nextNode); } push(nextNode); v = inspectCurrentTrace(); if ( v != null ) return v; } } catch (ViolatingPathFoundException vcfe ) { return vcfe.v; } } if ( iterationCount % iterationCountGap == 0 ) { listener.iterationCount(iterationCount, visited.getVisitedStateCount(), this); } } return null; } protected DfsTraversalNode getUnvisitedNextNode(DfsTraversalNode src, ExecutorService execSvc) throws ViolatingPathFoundException{ while (src.getEventIterator().hasNext()) { final BEvent nextEvent = src.getEventIterator().next(); try { DfsTraversalNode possibleNextNode = src.getNextNode(nextEvent, execSvc); visitedEdgeCount++; BProgramSyncSnapshot pns = possibleNextNode.getSystemState(); int stateIndexOnTrace = trace.indexOf(pns); if ( stateIndexOnTrace > -1 ) { // cycle found trace.cycleTo(nextEvent, stateIndexOnTrace); Set res = inspections.stream().map(i->i.inspectTrace(trace)) .filter(o->o.isPresent()).map(Optional::get).collect(toSet()); for ( Violation v : res ) { if ( ! listener.violationFound(v, this)) { throw new ViolatingPathFoundException(v); } } } else if ( visited.isVisited(pns) ) { // non cyclic, revisiting a state from a different path. // ... Quickly inspect the path and continue. trace.advance(nextEvent, pns); Set res = inspections.stream().map(i->i.inspectTrace(trace)) .filter(o->o.isPresent()).map(Optional::get).collect(toSet()); if ( res.size() > 0 ) { for ( Violation v : res ) { if ( ! listener.violationFound(v, this) ) { throw new ViolatingPathFoundException(v); } } } trace.pop(); } else { // advance to this newly discovered node return possibleNextNode; } } catch ( BPjsRuntimeException bprte ) { trace.advance(nextEvent, null); Violation jsev = new JsErrorViolation(trace, bprte); if ( ! listener.violationFound(jsev, this) ) { throw new ViolatingPathFoundException(jsev); } trace.pop(); } } return null; } public void setMaxTraceLength(long maxTraceLength) { this.maxTraceLength = maxTraceLength; } public long getMaxTraceLength() { return maxTraceLength; } public void setVisitedStateStore(VisitedStateStore aVisitedStateStore) { visited = aVisitedStateStore; } public VisitedStateStore getVisitedStateStore() { return visited; } public void setProgressListener(ProgressListener pl) { listener = (pl != null) ? pl : NULL_PROGRESS_LISTENER; } public void setIterationCountGap(long iterationCountGap) { this.iterationCountGap = iterationCountGap; } public long getIterationCountGap() { return iterationCountGap; } public BProgram getCurrentBProgram() { return currentBProgram; } void printStatus(long iteration, List path) { System.out.println("Iteration " + iteration); System.out.println(" visited: " + visited.getVisitedStateCount()); path.forEach(n -> System.out.println(" " + n.getLastEvent())); } private Violation inspectCurrentTrace() { Set res = inspections.stream() .map(v->v.inspectTrace(trace)) .filter(o->o.isPresent()).map(Optional::get) .collect(toSet()); if ( res.size() > 0 ) { for ( Violation v : res ) { if ( ! listener.violationFound(v, this) ) { return v; } } if (isDebugMode()) { System.out.println("-pop! (violation)-"); } pop(); } return null; } private void push(DfsTraversalNode n) { visited.store(n.getSystemState()); currentPath.add(n); if ( trace.getStateCount() == 0 ) { trace.push( n.getSystemState() ); } else { trace.advance(n.getLastEvent(), n.getSystemState()); } } private DfsTraversalNode pop() { DfsTraversalNode popped = currentPath.remove(currentPath.size() - 1); trace.pop(); return popped; } private int pathLength() { return currentPath.size(); } private boolean isPathEmpty() { return pathLength() == 0; } private DfsTraversalNode peek() { return isPathEmpty() ? null : currentPath.get(currentPath.size() - 1); } public boolean isDebugMode() { return debugMode; } public void setDebugMode(boolean debugMode) { this.debugMode = debugMode; } public I addInspection( I ins ) { inspections.add(ins); return ins; } public Set getInspections() { return inspections; } public boolean removeInspection( ExecutionTraceInspection ins ) { return inspections.remove(ins); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy