diff --git a/src/main/gov/nasa/jpf/search/Search.java b/src/main/gov/nasa/jpf/search/Search.java index e447741a..01fdb0b2 100644 --- a/src/main/gov/nasa/jpf/search/Search.java +++ b/src/main/gov/nasa/jpf/search/Search.java @@ -39,50 +39,113 @@ import java.util.concurrent.atomic.AtomicBoolean; /** - * the mother of all search classes. Mostly takes care of listeners, keeping - * track of state attributes and errors. This class mainly keeps the - * general search info like depth, configured properties etc. + * The {@code Search} abstract class is at the heart of all search classes. Even when it is not extended by child classes, it will + * be embedded in the logic as an input for some of the methods. + * + *

The main purpose of the {@code Search} class is to track general search information such as depth, configured properties, + * errors, etc. as well as to define how the search algorithm functions. In its simplest form, a search algorithm can be defined + * using the abstract method {@code search} with a series of {@code forward} and {@code backtrack}. More complex search + * algorithms can also make use of the state storing functionality of the {@code Search} class, as well as ignoring states and + * removing states from the search tree. + * + *

Another very important aspect of the {@code Search} class is not only to implement search algorithms, but also to define + * the information that describes the current search loop. This is crucial in order to enable the {@code SearchListener} and + * {@code SearchListenerAdapter} classes responsible for extracting crucial data from the search loop during its verification + * efforts. */ public abstract class Search { + /** The {@code JPFLogger} object assigned to the {@code gov.nasa.jpf.search} subsystem + * @see gov.nasa.jpf.util.JPFLogger + */ protected static JPFLogger log = JPF.getLogger("gov.nasa.jpf.search"); - /** error encountered during last transition, null otherwise */ + /** The value of {@code currentError} will either be the error encountered during last + * transition or null if no error was encountered */ protected Error currentError = null; + + /** A running list of all errors encountered during verification. The list will always + * contain the current and past values of {@code currentError}. Errors are set added + * during verification in the {@code error} + * method. + * + *

{@code errors} will only hold one error before verification stops unless {@code getAllErrors} + * is set to true + * + * @see #error(Property, Path, ThreadList) + * @see #getAllErrors + */ protected ArrayList errors = new ArrayList(); - protected int depth = 0; - protected VM vm; + /** {@code depth} represents the current depth of the search tree. */ + protected int depth = 0; + /** {@code vm} represents the virtual machine that the search algorithm will be traversing*/ + protected VM vm; + /** A list of properties provided from the search configuration at the start of verification. Every property has a {@code check} + * method that will be used during verification to tell if any of the properties have been violated.*/ protected ArrayList properties; + /** A property set by the user before verification. If {@code matchDepth} is true it will change the behavior of + * {@code isNewState()}. + * @see #isNewState() */ protected boolean matchDepth; + /** A property set by the user to define what the minimum memory required to keep verifying is. The reason this is a non-zero + * value is to ensure some memory is left over to output a legible message before ending execution instead of simply throwing + * an {@code OutOfMemoryError}. + * + *

The default value for {@code minFreeMemory} is 1024 << 10 bytes. */ protected long minFreeMemory; + /** A property set by the user to define what the maximum depth allowed during verification is. + * + *

By default the value is set to {@code Integer.MAX_VALUE}*/ protected int depthLimit; + + /** A property set by the user to define whether to halt execution after encountering the first error during verification. + * If {@code getAllErrors} is set to true, verification will not stop after the first error, and every possible error + * during verification will be logged as a result. */ protected boolean getAllErrors; - // message explaining the last search constraint hit + /** A {@code String} set to contain details on what the constraint that caused verification to halt was. Will generally be + * used by the {@code searchContraintHit} methods in {@code SearchListeners}. + * + * @see gov.nasa.jpf.search.SearchListener#searchConstraintHit(Search) */ protected String lastSearchConstraint; - // these states control the search loop + /** {@code done} is a flag set during verification to notify the search loop when verification has finished. */ protected boolean done = false; + /** {@code doBacktrack} is a flag set during verification in order to request that the search loop backtracks + * one state.*/ protected boolean doBacktrack = false; - // do we have a probe request + /** {@code notifyProbeListeners} is a flag set during verification stating whether or not a probe has been + * requested. When a probe is requested, relevant {@code SearchListeners} will be notified via {@code checkAndResetProbeRequest}*/ protected AtomicBoolean notifyProbeListeners = new AtomicBoolean(false); - /** search listeners. We keep them in a simple array to avoid + /** {@code listeners} is an array used to hold {@code SearchListener} objects. We keep them in a simple array to avoid creating objects on each notification */ protected SearchListener[] listeners = new SearchListener[0]; - /** this is a special SearchListener that is always notified last, so that - * PublisherExtensions can be sure the notification has been processed by all listeners */ + /** {@code reporter} is a special listener that is always notified last in order to ensure all other listeners have been notified + * beforehand. {@code reporter} is used to report information about the search loop to whichever output method the user has + * specified. + * + *

By default, the output method is to console.*/ protected Reporter reporter; - protected final Config config; // to later-on access settings that are only used once (not ideal) + /** {@code config} is an object passed in during instantiation of the {@code Search} object and it specifies the properties and + * configurations the search loop should run under. The value behind {@code config} is only ever used once and stored within this + * object.*/ + protected final Config config; - // don't forget to unregister or we have a HUGE memory leak if the same Config object is - // reused for several JPF runs + /** + * {@code ConfigListener} is an implementation of the interface {@code ConfigChangeListener} and is used to subscribe to changes + * that occur to the configuration of JPF in order to update the {@code config} object. While highly useful, one should always + * remember to unregister the {@code config} object from each {@code ConfigListener} once it is no longer needed. Failure to + * do this will cause massive memory leaks to begin piling up as a result of the same {@code Config} object being used across + * several JPF executions. + * + */ class ConfigListener implements ConfigChangeListener { @Override @@ -109,9 +172,19 @@ public void jpfRunTerminated (Config config){ } } - /** storage to keep track of state depths */ + /** {@code stateDepth} is an int vector storage system (akin to a map) that is responsible for associating states with their + * corresponding depths. + * + *

{@code stateDepth} maps the state id of every state to the corresponding depth, in a one to one mapping.*/ protected final IntVector stateDepth = new IntVector(); + /** + * Constructs a {@code Search} object using the configuration specified by the user in {@code config} and a virtual machine + * that will be used for verification. + * + * @param config The configuration and properties that the search loop will be required to commence verification under + * @param vm The virtual machine that the search loop will traverse and verify states from + */ protected Search (Config config, VM vm) { this.vm = vm; this.config = config; @@ -126,6 +199,12 @@ protected Search (Config config, VM vm) { config.addChangeListener( new ConfigListener()); } + /** + * Initializes the properties that the search loop will need to run under in order to make sure that each iteration of the search + * loop is in compliance with the properties set by JPF and the user. + * + * @param conf The configuration object ({@code config}) that contains the necessary information to initialize the property values + */ protected void initialize( Config conf){ depthLimit = conf.getInt("search.depth_limit", Integer.MAX_VALUE); matchDepth = conf.getBoolean("search.match_depth"); @@ -134,53 +213,103 @@ protected void initialize( Config conf){ } /** - * called after the JPF run is finished. Shouldn't be public, but is called by JPF + * Called after the JPF run is finished. Should not be public, but is called by JPF */ public void cleanUp(){ // nothing here, the ConfigListener removes itself } + /** + * Returns the {@code config} object that is used by the {@code Search} class. + * + * @return The configuration object that is used for verification + */ public Config getConfig() { return config; } + /** + * One of the most important methods in the {@code Search} class. {@code search} is used to specify what the algorithm and behavior + * for verification are. When the term "search loop" is brought up in other documentation, it generally means the loop that is used + * running in the {@code search} method. + */ public abstract void search (); + /** + * Sets the {@code reporter} object used during verification to the specified {@code reporter}. + * + * @param reporter The reporter used to replace the current reporter object in the {@code Search} class + */ public void setReporter(Reporter reporter){ this.reporter = reporter; } + /** + * Appends a {@code SearchListener} object to the {@code listener} array. In addition to appending the search listener, the action is also logged + * to the JPFLog {@code log} used in the {@code Search} class. + * + * @param newListener A new {@code SearchListener} to append to the {@code listener} array + */ public void addListener (SearchListener newListener) { log.info("SearchListener added: ", newListener); listeners = Misc.appendElement(listeners, newListener); } + /** + * Checks whether a listener of the same type as {@code listenerCls} exists in {@code listeners} already. + * + * @param listenerCls The object type to check {@code listeners} against + * @return true if an element of the same type as the input parameter exists in {@code listeners}. False otherwise + */ public boolean hasListenerOfType (Class listenerCls) { return Misc.hasElementOfType(listeners, listenerCls); } + /** + * Returns the next element in {@code listeners} after the element matching {@code prev} that is of the same type as {@code type}. + * + * @param type The type of element to search for and return + * @param prev The previous element to begin the search from + * @return An element of the same class as {@code type} and coming after {@code prev} in the {@code listeners} array + */ public T getNextListenerOfType(Class type, T prev){ return Misc.getNextElementOfType(listeners, type, prev); } - + /** + * Remove the specified {@code SearchListener} from the {@code listeners} array. + * + * @param removeListener The element to remove from the {@code listeners} array + */ public void removeListener (SearchListener removeListener) { listeners = Misc.removeElement(listeners, removeListener); } - + /** + * Add a new property to {@code properties} + * + * @param newProperty The property to add to {@code properties} + */ public void addProperty (Property newProperty) { properties.add(newProperty); } + /** + * Remove the specified property from {@code properties} + * + * @param oldProperty The property to remove from {@code properties} + */ public void removeProperty (Property oldProperty) { properties.remove(oldProperty); } /** - * return set of configured properties - * note there is a name clash here - JPF 'properties' have nothing to do with - * Java properties (java.util.Properties) + * Returns the list of configured properties. + * + *

Note there is a name clash here - JPF 'properties' have nothing to do with Java properties (java.util.Properties) + * + * @param config The config object to retrieve the properties list from + * @return The list of configured properties */ protected ArrayList getProperties (Config config) { Class[] argTypes = { Config.class, Search.class }; @@ -192,7 +321,12 @@ protected ArrayList getProperties (Config config) { return list; } - // check for property violation, return true if not done + /** + * Check for property violations and return true if a property is violated and the search loop has finished running (i.e. {@code done} + * is set to true). Returns false otherwise. + * + * @return true if an a property has been violated (i.e. {@code currentError} is not null) and {@code done} is true + */ protected boolean hasPropertyTermination () { if (currentError != null){ if (done){ @@ -205,7 +339,12 @@ protected boolean hasPropertyTermination () { return false; } - // this should only be called once per transition, otherwise it keeps adding the same error + /** + * Iterates through {@code properties} and checks for property violations. {@code checkPropertyViolation} should only be + * called once per transition to avoid it adding the same error every time it is called. + * + * @return true if a property violation is found, false otherwise + */ protected boolean checkPropertyViolation () { for (Property p : properties) { if (!p.check(this, vm)) { @@ -217,24 +356,39 @@ protected boolean checkPropertyViolation () { return false; } + /** + * Returns the list of errors encountered during verification + * + * @return The list of errors encountered during verification + */ public List getErrors () { return errors; } + /** + * Returns the number of errors encountered during verification. Will always return 1 if {@code getAllErrors} is false + * + * @return The number of errors encountered during verification + */ public int getNumberOfErrors(){ return errors.size(); } + /** + * Returns the search constraint that was most recently encountered + * + * @return A String detailing the search constraint most recently hit or null if none was encountered + */ public String getLastSearchConstraint() { return lastSearchConstraint; } /** - * request a probe + * Request a probe * - * This does not do the actual listener notification, it only stores + *

This does not perform the actual listener notification, it only stores * the request, which is then processed from within JPFs inner execution loop. - * As a consequence, probeSearch() can be called async, and searchProbed() listeners + * As a consequence, {@code probeSearch} can be called asynchronously, and {@code searchProbed} listeners * don't have to bother with synchronization or inconsistent JPF states (notification * happens from within JPFs main thread after a completed Instruction execution) */ @@ -243,8 +397,7 @@ public void probeSearch(){ } /** - * this does the actual notification and resets the request, hence this call - * should only happen from within JPFs main thread + * Performs the actual notification and resets the request, hence this call should only happen from within JPFs main thread */ public void checkAndResetProbeRequest(){ if (notifyProbeListeners.compareAndSet(true, false)){ @@ -253,12 +406,19 @@ public void checkAndResetProbeRequest(){ } /** - * @return error encountered during *last* transition (null otherwise) + * Returns the most recent error encountered during the last transition + * + * @return The error encountered during the last transition or null if none was encountered */ public Error getCurrentError(){ return currentError; } + /** + * Returns the most recent error encountered in the past + * + * @return The most recent error encountered in the past or none if no error has been encountered during verification + */ public Error getLastError() { int i=errors.size()-1; if (i >=0) { @@ -268,30 +428,69 @@ public Error getLastError() { } } + /** + * Returns whether or not the search loop has encountered an error yet + * + * @return true if an error has been encountered, false otherwise + */ public boolean hasErrors(){ return !errors.isEmpty(); } + /** + * Returns the {@code vm} object used by the search loop + * + * @return the {@code vm} object used by {@code Search} + */ public VM getVM() { return vm; } + /** + * Returns true if the search loop has transitioned into an end state in the virtual machine + * + * @return true if the current state of the {@code vm} is an end state + */ public boolean isEndState () { return vm.isEndState(); } + /** + * Returns if an error has been encountered during the transition into the current state + * + * @return true if an error has been encountered during the most recent transition, false otherwise + */ public boolean isErrorState(){ return (currentError != null); } + /** + * Returns whether of not the current state is an end state in the virtual machine + * + * @return true if this is not an end state of the {@code vm}, false otherwise + */ public boolean hasNextState () { return !isEndState(); } + /** + * Returns whether a transition has occurred from the previous state or not + * + * @return true if a transition has occurred, false otherwise + */ public boolean transitionOccurred(){ return vm.transitionOccurred(); } + /** + * Returns whether the current state in the search loop is a new state and false otherwise. + * + *

However, if {@code matchDepth} is set to true, + * then it will true if the state is new or if the states depth is less than the previously recorded depth. If the state is new, the states + * depth will also be recorded for future use. + * + * @return true if the current state is a new state and false otherwise (behaviour changes if {@code matchDepth} is true + */ public boolean isNewState () { boolean isNew = vm.isNewState(); @@ -308,53 +507,111 @@ public boolean isNewState () { return isNew; } + /** + * Returns whether the current state has been visited yet. The opposite of {@code isNewState} + * + * @return true if the state has been visited, false otherwise (behavior changes if {@code matchDepth} is true) + * @see #isNewState() + */ public boolean isVisitedState () { return !isNewState(); } + /** + * Returns whether the current state is an ignored state in the virtual machine + * + * @return true if the current state is an ignored state, false otherwise + */ public boolean isIgnoredState(){ return vm.isIgnoredState(); } + /** + * Returns whether the current state has been fully explored by the search loop. + * + * @return true if the current state has been fully explored and processed, false otherwise + */ public boolean isProcessedState(){ return vm.getChoiceGenerator().isProcessed(); } + /** + * Return whether the search loop has finished verification + * + * @return true if the search loop has finished verification, false otherwise + */ public boolean isDone(){ return done; } + /** + * Return the current depth of the search loop + * + * @return The current depth inside the search tree. + */ public int getDepth () { return depth; } + /** + * Returns the most recent search constraint that was encountered + * + * @return A String detailing the most recent search constraint that was encountered + */ public String getSearchConstraint () { return lastSearchConstraint; } + /** + * Returns the most recent transition that has occurred + * + * @return The last transition that occurred during verification, or null if none has occurred + */ public Transition getTransition () { return vm.getLastTransition(); } + /** + * Returns the state id of the current state + * + * @return The state id of the current state + */ public int getStateId () { return vm.getStateId(); } + /** + * Returns the purged states id + * + *

Note that while it should return the purged states id, it currently only returns -1 as a + * result of many searches not utilizing this functionality. If it is required, {@code getPurgedStateId} + * should be overridden and changed in subsequent child classes. + * + * @return The purged states id (currently only returns -1) + */ public int getPurgedStateId () { return -1; // a lot of Searches don't purge any states } /** - * this is somewhat redundant to SystemState.setIgnored(), but we don't + * Requests that the search loop backtracks one step + * + *

This is somewhat redundant to {@code SystemState.setIgnored}, but we don't * want to mix the case of overriding state matching with backtracking when - * searching for multiple errors + * searching for multiple errors. + * + * @return The value of {@code doBacktrack} after is has been set to true (i.e. always true) */ public boolean requestBacktrack () { return doBacktrack = true; } - + /** + * Returns the value of {@code doBacktrack} as well as resetting {@code doBacktrack} to false + * + * @return the value of {@code doBackTrack} + */ protected boolean checkAndResetBacktrackRequest() { if (doBacktrack){ doBacktrack = false; @@ -364,32 +621,82 @@ protected boolean checkAndResetBacktrackRequest() { } } + /** + * Returns whether the search algorithm supports backtracking or not + * + * @return true if the search algorithm supports backtracking, false otherwise (by default the return + * is true unless overridden) + */ public boolean supportsBacktrack () { return true; } + /** + * Returns whether the search algorithm supports restoring states that have been stored (a useful method in + * BreadthFirstSearch) + * + * @return true if the search algorithm supports restoring states, false otherwise (by default the return + * is false as the function is unsupported) + */ public boolean supportsRestoreState () { // not supported by default return false; } + /** + * Returns the depth limit set at the beginning of verification + * + * @return The {@code depthLimit} property + */ public int getDepthLimit () { return depthLimit; } + /** + * Sets a new value for the {@code depthLimit} property + * + * @param limit The new limit to set {@code depthLimit} to + */ public void setDepthLimit(int limit){ depthLimit = limit; } + /** + * Returns a {@code SearchState} object with information describing the current state in the search loop + * + * @return A {@code SearchState} object with information about the current state + */ protected SearchState getSearchState () { return new SearchState(this); } - // can be used by SearchListeners to create path-less errors (liveness) + /** + * Creates a new error with the corresponding {@code Property} that causes the error. Does not provide a {@code Path} or {@code ThreadList}, and + * can therefore be used by {@code SearchListeners} to create path-less errors to ensure liveness. + * + * @param property the property that caused the error + * @see #error(Property, Path, ThreadList) + */ public void error (Property property) { error(property, null, null); } + /** + * Creates an error with the corresponding {@code Property} that causes the error, alongside the {@code Path}, and {@code ThreadList}. + * + *

Will set {@code done} to true and halt the search loop if {@code getAllErrors} is false. If {@code getAllErrors} is true, it will + * clone the {@code Property}, {@code Path}, and {@code ThreadList} objects that were passed in (as not cloning them may cause subsequent + * operations to overwrite information still in use) and add them to the {@code errors} list + * and continue verification. + * + *

The property that caused the error is not reset here as the listeners attached to the search should be notified of the error first. + * This becomes especially problematic if one of the listeners caused the property violation, as which point it would get confused + * if the {@code propertyViolated()} notification happens after the property is reset. + * + * @param property The property that causes the error + * @param path The path corresponding to the error + * @param threadList The list of thread information corresponding to the error + */ public void error (Property property, Path path, ThreadList threadList) { if (getAllErrors) { @@ -411,17 +718,20 @@ public void error (Property property, Path path, ThreadList threadList) { errors.add(currentError); - // we should not reset the property until listeners have been notified - // (the listener might be the property itself, in which case it could get - // confused if propertyViolated() notifications happen after a reset) } + /** + * Resets all properties, returning violated properties to their default states. + */ public void resetProperties(){ for (Property p : properties) { p.reset(); } } + /** + * Notifies the {@code SearchListener} objects in the {@code listeners} list that the search loop has advanced states. + */ protected void notifyStateAdvanced () { try { for (int i = 0; i < listeners.length; i++) { @@ -436,6 +746,10 @@ protected void notifyStateAdvanced () { } } + /** + * Notifies the {@code SearchListener} objects in the {@code listeners} list that the search loop has finished processing + * the current state. + */ protected void notifyStateProcessed() { try { for (int i = 0; i < listeners.length; i++) { @@ -449,6 +763,9 @@ protected void notifyStateProcessed() { } } + /** + * Notifies the {@code SearchListener} objects in the {@code listeners} list that the search loop has stored the current state. + */ protected void notifyStateStored() { try { for (int i = 0; i < listeners.length; i++) { @@ -462,6 +779,10 @@ protected void notifyStateStored() { } } + /** + * Notifies the {@code SearchListener} objects in the {@code listeners} list that the search loop has restored a currently + * stored state. + */ protected void notifyStateRestored() { try { for (int i = 0; i < listeners.length; i++) { @@ -475,6 +796,10 @@ protected void notifyStateRestored() { } } + /** + * Notifies the {@code SearchListener} objects in the {@code listeners} list that the search loop has backtracked to a + * previous state. + */ protected void notifyStateBacktracked() { try { for (int i = 0; i < listeners.length; i++) { @@ -488,6 +813,9 @@ protected void notifyStateBacktracked() { } } + /** + * Notifies the {@code SearchListener} objects in the {@code listeners} list that the search loop has purged the current state. + */ protected void notifyStatePurged() { try { for (int i = 0; i < listeners.length; i++) { @@ -501,6 +829,10 @@ protected void notifyStatePurged() { } } + /** + * Notifies the {@code SearchListener} objects in the {@code listeners} list that a probe request has been triggered during + * verification. + */ public void notifySearchProbed() { try { for (int i = 0; i < listeners.length; i++) { @@ -515,6 +847,10 @@ public void notifySearchProbed() { } + /** + * Notifies the {@code SearchListener} objects in the {@code listeners} list that the search loop has encountered a property + * violation + */ protected void notifyPropertyViolated() { try { for (int i = 0; i < listeners.length; i++) { @@ -533,6 +869,9 @@ protected void notifyPropertyViolated() { } } + /** + * Notifies the {@code SearchListener} objects in the {@code listeners} list that the search loop has begun verification. + */ protected void notifySearchStarted() { try { for (int i = 0; i < listeners.length; i++) { @@ -546,6 +885,13 @@ protected void notifySearchStarted() { } } + /** + * Notifies the {@code SearchListener} objects in the {@code listeners} list that the search loop has encountered a + * search constraint. The {@code details} String outlines the search constraint that was hit as well as any other + * relevant information. + * + * @param details Information regarding the recent search constraint that was encountered. + */ public void notifySearchConstraintHit(String details) { try { lastSearchConstraint = details; @@ -560,6 +906,9 @@ public void notifySearchConstraintHit(String details) { } } + /** + * Notifies the {@code SearchListener} objects in the {@code listeners} list that the search loop has finished verification. + */ protected void notifySearchFinished() { try { for (int i = 0; i < listeners.length; i++) { @@ -573,6 +922,15 @@ protected void notifySearchFinished() { } } + /** + * Requests that the virtual machine move to the next unvisited state below the current one in the search tree. + * {@code forward} along with {@code backtrack} constitute the two methods that are generally used to advance + * the search algorithm during verification. + * + * @return true if a state exists and the virtual machine can move to it, false if the state does not exist + * or if it was previously explored. + * @see #backtrack() + */ protected boolean forward () { currentError = null; @@ -581,28 +939,76 @@ protected boolean forward () { checkPropertyViolation(); return ret; } - + + /** + * Requests that the virtual machine move to the previous state in the search tree. {@code backtrack} along + * with {@code forward} constitute the two methods that are generally used to advance the search algorithm + * during verification. + * + * @return true if the backtrack to the previous state was successful, false otherwise + */ protected boolean backtrack () { return vm.backtrack(); } + /** + * Requests that the virtual machine sets whether the current state should be ignored in all future iterations + * of the search loop. This should not be used without cause as it causes the search tree to be pruned whenever + * it is called, which is expensive, especially in larger trees. + * + * @param cond Whether or not the current state should be ignored + */ public void setIgnoredState (boolean cond) { vm.ignoreState(cond); } + /** + * Restores a previously stored virtual machine state. + * + *

By default this method is not supported, and therefore requires implementation logic in child classes. + * + * @param state The stored state to be restored + */ protected void restoreState (State state) { // not supported by default } - /** this can be used by listeners to terminate the search */ + /** + * Sets {@code done} to true in order to terminate the search loop. + * + *

Can be used by listeners to terminate the search. + * + */ public void terminate () { done = true; } + /** + * Sets the depth of the specified state given its state id. + * + *

When the depth of the state is set, it is set as depth + 1. This is to differentiate between + * states that have had their depths set, and states that have not (in which case their default depth + * will be 0). + * + * @param stateId The state in question + * @param depth The new depth to set for the state + */ protected void setStateDepth (int stateId, int depth) { stateDepth.set(stateId, depth + 1); } + + /** + * Returns the saved depth of the specified state given its state id. + * + *

If the state in question has not had its state depth set previously, then it will by default have + * a depth that is less than or equal to 0. Otherwise if the state has been visited, the depth will be + * returned. + * + * @param stateId The state to return the depth of + * @return The depth of the specified state + * @throws JPFException If the state has not been visited in the past + */ public int getStateDepth (int stateId) { int depthPlusOne = stateDepth.get(stateId); if (depthPlusOne <= 0) { @@ -613,9 +1019,15 @@ public int getStateDepth (int stateId) { } /** - * check if we have a minimum amount of free memory left. If not, we rather want to stop in time - * (with a threshold amount left) so that we can report something useful, and not just die silently - * with a OutOfMemoryError (which isn't handled too gracefully by most VMs) + * Check if there is the minimum amount of free memory left or more. If not, we would rather stop in time + * (with a threshold amount left) in order to report something useful, and not just end verification silently + * with a OutOfMemoryError (which is not handled too gracefully by most VMs) + * + *

If the minimum amount of memory has been reached, the method will first try to activate garbage collection + * and then check again if that made a difference. If the amount of memory available is still less than the + * minimum amount of memory, then we return false. + * + * @return true if we have more memory than the minimum free memory, false otherwise. */ public boolean checkStateSpaceLimit () { Runtime rt = Runtime.getRuntime(); diff --git a/src/main/gov/nasa/jpf/search/SearchListener.java b/src/main/gov/nasa/jpf/search/SearchListener.java index c137ecc0..12dc6911 100644 --- a/src/main/gov/nasa/jpf/search/SearchListener.java +++ b/src/main/gov/nasa/jpf/search/SearchListener.java @@ -20,72 +20,156 @@ import gov.nasa.jpf.JPFListener; /** - * interface to register for notification by the Search object. - * Observer role in same-name pattern + * The {@code SearchListener} interface defines the methods available to register for notifications by the {@code Search} object. + * + *

Any desired methods will require implementation logic in the child class, but unwanted methods can be instantiated without logic. + * {@code SearchListenerAdapter} instantiates all of the methods as an abstract class and does not require child classes to instantiate all methods. + * If the child class is not another {@code Listener} type interface, then {@code SearchListenerAdapter} would be a more appropriate parent class. + * + *

This interface is to be used alongside classes that extend {@code Search} class functionality. {@code SearchListener} is capable of gauging + * {@code Search} attributes through the implemented methods and can receive information such as depth, configured properties, and other important {@code Search} + * properties. */ + public interface SearchListener extends JPFListener { - - /** - * got the next state - * Note - this will be notified before any potential propertyViolated, in which - * case the currentError will be already set - */ - void stateAdvanced (Search search); - - /** - * state is fully explored - */ - void stateProcessed (Search search); - - /** - * state was backtracked one step - */ - void stateBacktracked (Search search); - /** - * some state is not going to appear in any path anymore - */ - void statePurged (Search search); + /** + * Notified when the state in a {@code Search} child class is advanced forward + * one state in the search tree. The {@code Search} object that is passed as + * a parameter contains attributes describing the next state in the search + * loop as well as attributes describing the specifications of the search loop + * itself (e.g. current depth, depth limit, current error, etc.) + * + *

{@code stateAdvanced(Search search)} will be notified and called before any potential + * property violations (in {@code propertyViolated(Search search)}, thus the currentError + * will already be set and may not reflect upcoming property violations. + * + * @param search a {@code Search} object that denotes current attributes in the next state + */ + void stateAdvanced (Search search); + + /** + * Notified when the search loop has fully explored the current state and is ready to move + * to a new state in the search tree. The {@code Search} object that is passed as a parameter + * contains attributes describing the current state in the search loop as well as attributes + * describing the specifications of the search loop itself (e.g. current depth, depth limit, + * current error, etc.) + * + * @param search a {@code Search} object that denotes current attributes in the current state + */ + void stateProcessed (Search search); + + /** + * Notified when the state in a {@code Search} child class is moved backwards + * one state in the search tree. The {@code Search} object that is passed as + * a parameter contains attributes describing the next state (which is also + * the previous state) in the search tree attributes as well as attributes + * describing the specifications of the search loop itself (e.g. current depth, + * depth limit, current error, etc.) + * + * @param search a {@code Search} object that denotes current attributes + * of the next (previous) state + */ + void stateBacktracked (Search search); + + /** + * Notified when a state is removed from the search tree and will not be appearing + * in the future. The {@code Search} object passed through contains details + * that describe the state and the current properties of the search loop + * (e.g. current depth, depth limit, current error, etc.) + * + * @param search a {@code Search} object that denotes current attributes of the purged state + */ + void statePurged (Search search); + + /** + * Notified when an explored state from the search state is stored to be accessed later. The + * {@code Search} object passed through contains details that describe the state and current + * properties of the search loop (e.g. current depth, depth limit, current error, etc.) + * + *

The stored state may be restored later through {@code stateRestored(Search search)} + * + * @param search a {@code Search} object that denotes current attributes of the stored state + */ + void stateStored (Search search); + + /** + * Notified when a previously stored search state is restored for use in the search algorithm. + * The {@code Search} object passed through contains details that describe the restored state + * and current properties of the search loop (e.g. current depth, depth limit, current + * error, etc.) + * + *

The restored state is guaranteed to have been visited and explored in the past. + * + *

The restored state may be from a separate path than the previous state. + * + * @param search a {@code Search} object that denotes current attributes of the restored state + */ + void stateRestored (Search search); + + /** + * Notified when a probe request occurs (e.g. from a periodical timer). The {@code Search} object + * passed through contains details that describe the probe request, as well as the properties of + * the search loop (e.g. current depth, depth limit, current error, etc.) + * + * While probe requests in {@code Search} may be implemented and called asynchronously, the notification + * sent to {@code searchProbed(Search search)} will always be from the main JPF loop, and thus be synchronously + * called (i.e. after instruction execution). + * + * @param search a {@code Search} object that denotes current attributes of the probe request + */ + void searchProbed (Search search); + + /** + * Notified when JPF encounters a property violation of the application during the search loop. The + * {@code Search} object passed through contains details describing the properties of the search loop + * (e.g. current depth, depth limit, current error, etc.) + * + *

Notification of {@code stateAdvanced(Search search)} will always precede notifications of + * {@code propertyViolated(Search search)} + * + *

The JPF search loop will notify {@code SearchListener} of the property violation before resetting the violation + * + *

Properties refers to jpf.Property and not java.util.Property + * + * @param search a {@code Search} object that denotes current attributes at the time of the property violation + */ + void propertyViolated (Search search); + + /** + * Notified when a search is started and the search loop is entered. The {@code Search} object passed through + * contains details describing the properties of the search loop (e.g. current depth, depth limit, current + * error, etc.) + * + *

{@code searchStarted(Search search)} will always be called before the first {@code Search.forward()} call. + * + * @param search a {@code Search} object that denotes current attributes of search loop + */ + void searchStarted (Search search); + + /** + * Notified when a constraint is reached during the search loop. The {@code Search} object passed through contains details describing + * the properties of the search loop at the time of notification (e.g. current depth, depth limit, current error, etc.) + * + *

The constraint being reached may have also been turned into a property but is usually an attribute of the search, not the application. + * + *

Examples of constraints are depth limit or the amount of memory that the search loop is allowed to use being exceeded. These general constraints + * are usually specified in a properties file. + * + * @param search a {@code Search} object that denotes current attributes of search loop at the time of the constraint being hit + */ + void searchConstraintHit (Search search); - /** - * somebody stored the state - */ - void stateStored (Search search); - - /** - * a previously generated state was restored - * (can be on a completely different path) - */ - void stateRestored (Search search); - - /** - * there was a probe request, e.g. from a periodical timer - * note this is called synchronously from within the JPF execution loop - * (after instruction execution) - */ - void searchProbed (Search search); - - /** - * JPF encountered a property violation. - * Note - this is always preceeded by a stateAdvanced - */ - void propertyViolated (Search search); - - /** - * we get this after we enter the search loop, but BEFORE the first forward - */ - void searchStarted (Search search); - - /** - * there was some contraint hit in the search, we back out - * could have been turned into a property, but usually is an attribute of - * the search, not the application - */ - void searchConstraintHit (Search search); - - /** - * we're done, either with or without a preceeding error - */ - void searchFinished (Search search); + /** + * Notified when a search ends and the search loop is exited. The {@code Search} object passed through + * contains details describing the properties of the search loop and final state (e.g. current depth, + * depth limit, current error, etc.) + * + *

If the search was finished prematurely due to some unexpected reason, a preceding error may antecede + * the {@code searchFinished(Search search)} call. + * + * @param search a {@code Search} object that denotes attributes of finalized search loop + */ + void searchFinished (Search search); } diff --git a/src/main/gov/nasa/jpf/search/SearchListenerAdapter.java b/src/main/gov/nasa/jpf/search/SearchListenerAdapter.java index 14933361..9f46462d 100644 --- a/src/main/gov/nasa/jpf/search/SearchListenerAdapter.java +++ b/src/main/gov/nasa/jpf/search/SearchListenerAdapter.java @@ -19,8 +19,14 @@ package gov.nasa.jpf.search; /** - * a no-action SearchListener which we can use to override only the - * notifications we are interested in + * The {@code SearchListenerAdapter} abstract class instantiates the methods from {@code SearchListener} in order to create an adapter design pattern. + * + *

Any desired methods will require implementing logic in child classes, however unwanted methods can be left uninstantiated in order to aid in + * code readability. + * + *

This class is to be used alongside classes that extend {@code Search} class functionality. {@code SearchListenerAdapter} is capable of gauging + * Search attributes through the implemented methods and can receive information such as depth, configured properties, and other important {@code Search} + * attributes. */ public class SearchListenerAdapter implements SearchListener {