//
// Copyright (C) 2006 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.listener;
import java.io.PrintWriter;
import java.util.Formatter;
import gov.nasa.jpf.Config;
import gov.nasa.jpf.ListenerAdapter;
import gov.nasa.jpf.search.Search;
import gov.nasa.jpf.vm.ChoiceGenerator;
import gov.nasa.jpf.vm.Path;
import gov.nasa.jpf.vm.Transition;
import gov.nasa.jpf.vm.VM;
/**
* From already visited states, estimates the total number of states by the branching factor.
*/
public class StateCountEstimator extends ListenerAdapter
{
private final PrintWriter m_out;
private final StringBuilder m_buffer = new StringBuilder();
private final Formatter m_formatter = new Formatter(m_buffer);
private final int m_logPeriod;
private double m_lastPercent;
private long m_nextLog;
private long m_startTime;
public StateCountEstimator(Config config)
{
m_out = new PrintWriter(System.out, true);
m_logPeriod = config.getInt("jpf.state_count_estimator.log_period", 0);
}
@Override
public void searchStarted(Search search)
{
m_nextLog = 0;
m_lastPercent = 0.0;
m_startTime = System.currentTimeMillis();
}
@Override
public void searchFinished(Search search)
{
log(search);
}
@Override
public void stateProcessed(Search search)
{
if (m_nextLog > System.currentTimeMillis())
return;
if (log(search))
m_nextLog = m_logPeriod + System.currentTimeMillis();
}
private boolean log(Search search)
{
VM vm;
Path path;
Transition trans;
ChoiceGenerator<?> cg;
double percent, delta;
long currentState, expectedState, currentTime, expectedTime;
int i, size, processed;
vm = search.getVM();
path = vm.getPath();
size = path.size();
percent = 0.0;
delta = 1.0;
processed = 0;
for (i = 0; i < size; i++)
{
trans = path.get(i);
cg = trans.getChoiceGenerator();
delta /= cg.getTotalNumberOfChoices();
processed = cg.getProcessedNumberOfChoices() - 1;
percent += delta * processed;
}
if (size == 0)
percent = 1.0;
if (m_lastPercent > percent) // Sometimes a state is declared as processed but doesn't show up in the path so the percentage appears to go backwards.
return(false);
m_lastPercent = percent;
currentState = vm.getStateCount();
expectedState = (long) (currentState / percent);
currentTime = System.currentTimeMillis() - m_startTime;
expectedTime = (long) (currentTime / percent);
m_formatter.format("State: %,d / %,d (%g%%) Time: ", currentState, expectedState, 100.0 * percent);
formatTime(expectedTime);
m_buffer.append(" - ");
formatTime(currentTime);
m_buffer.append(" = ");
formatTime(expectedTime - currentTime);
m_out.println(m_buffer.toString());
m_buffer.setLength(0);
return(true);
}
private void formatTime(long time)
{
long days, hours, minutes, seconds;
boolean commit;
seconds = time / 1000;
minutes = seconds / 60;
hours = minutes / 60;
days = hours / 24;
seconds %= 60;
minutes %= 60;
hours %= 24;
commit = false;
if ((commit) || (days != 0))
{
commit = true;
m_buffer.append(days);
m_buffer.append(' ');
}
if ((commit) || (hours != 0))
{
if ((commit) && (hours < 10))
m_buffer.append('0');
m_buffer.append(hours);
m_buffer.append(':');
commit = true;
}
if ((commit) || (minutes != 0))
{
if ((commit) && (minutes < 10))
m_buffer.append('0');
m_buffer.append(minutes);
m_buffer.append(':');
commit = true;
}
if ((commit) && (seconds < 10))
m_buffer.append('0');
m_buffer.append(seconds);
}
}