/*
* This file is part of JOP, the Java Optimized Processor
* see <http://www.jopdesign.com/>
*
* Copyright (C) 2010, Benedikt Huber (benedikt.huber@gmail.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jopdesign.wcet.uppaal;
import com.jopdesign.common.config.Config;
import com.jopdesign.wcet.WCETTool;
import org.apache.log4j.Logger;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* Binary search for WCET using UppAal
* @author Benedikt Huber <benedikt.huber@gmail.com>
*
*/
public class WcetSearch {
private File modelFile;
private File queryFile;
private Logger logger = Logger.getLogger(WCETTool.LOG_WCET_UPPAAL+".WcetSearch");
private double maxSolverTime = 0.0;
private Config config;
public double getMaxSolverTime() {
return maxSolverTime;
}
public WcetSearch(Config c, File modelFile) {
this.config = c;
this.modelFile = modelFile;
}
public long searchWCET(Long upperBound) throws IOException {
long ub = (upperBound == null) ? -1 : upperBound;
queryFile = File.createTempFile("query", ".q");
List<String> cmdlist = new ArrayList<String>();
cmdlist.add(config.getOption(UppAalConfig.UPPAAL_VERIFYTA_BINARY));
cmdlist.add("-q");
cmdlist.add("-S");
cmdlist.add("2");
cmdlist.add("-o");
cmdlist.add("2");
if(config.getOption(UppAalConfig.UPPAAL_CONVEX_HULL)) {
cmdlist.add("-A");
}
cmdlist.add(modelFile.getPath());
cmdlist.add(queryFile.getPath());
String[] cmd = cmdlist.toArray(new String[cmdlist.size()]);
long safe, unsafe;
if(ub >= 1) {
unsafe = safe = ub;
while(checkBound(cmd,unsafe)) {
safe = unsafe;
unsafe /= 2;
System.err.println(MessageFormat.format("WCET bounds (? / safe): {0}/{1}",
unsafe, safe));
}
System.err.println(MessageFormat.format("WCET bounds (unsafe / safe): {0}/{1}",
unsafe, safe));
} else {
unsafe = 1;
safe = 100;
while(! checkBound(cmd,safe)) {
unsafe = safe;
safe *= 2;
System.err.println(MessageFormat.format("WCET bounds (unsafe/safe): {0}/{1}",
unsafe, safe));
}
}
while(unsafe + 1 < safe) {
long m = unsafe+((safe-unsafe)/2);
if(checkBound(cmd,m)) { /* m is a safe bound */
safe = m;
} else {
unsafe = m;
}
System.err.println(MessageFormat.format("WCET bounds (unsafe/safe): {0}/{1}",
unsafe, safe));
}
return safe;
}
private static class StreamReaderThread extends Thread {
private List<String> data = null;
private BufferedReader reader;
private int limit;
private LinkedList<String> dataList = null;
private boolean doEcho = false;
private int doStatusEcho;
public StreamReaderThread(InputStream inputStream) {
this.reader = new BufferedReader(new InputStreamReader(inputStream));
this.data = new ArrayList<String>();
this.limit = -1;
}
public StreamReaderThread(InputStream inputStream,int limit) {
this.reader = new BufferedReader(new InputStreamReader(inputStream));
this.limit = limit;
this.dataList = new LinkedList<String>();
}
@Override
public void run() {
String l ;
try {
while((l = reader.readLine()) != null) {
if(doEcho) System.out.println(l);
if(doStatusEcho > 0) {
System.out.print(".");
System.out.flush();
}
process(l);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public List<String> getData() {
if(dataList != null) {
data = new ArrayList<String>(dataList);
}
return data;
}
protected void process(String l) {
if(dataList == null) data.add(l);
else {
dataList.offer(l);
if(dataList.size() > limit) dataList.remove();
}
}
public String getMessage() {
return this.data.toString();
}
public void setEcho(boolean b) {
doEcho = b;
}
public void setStatusEcho(boolean b) {
doStatusEcho = 1;
}
}
private boolean checkBound(String[] cmd, long m) throws IOException {
writeQueryFile(queryFile, m);
long start = System.nanoTime();
Process verifier = Runtime.getRuntime().exec(cmd);
StreamReaderThread outLines = new StreamReaderThread(verifier.getInputStream(),3);
//outLines.setEcho(true);
outLines.setStatusEcho(true);
outLines.run();
StreamReaderThread errLines = new StreamReaderThread(verifier.getErrorStream());
errLines.run();
try {
if(verifier.waitFor() != 0) {
logger.error("verifyta: "+errLines.getMessage());
throw new IOException("Uppaal verifier terminated with exit code: "+verifier.exitValue());
} else {
long stop = System.nanoTime();
maxSolverTime = Math.max(maxSolverTime,((double)(stop-start)) / 1.0E9);
}
} catch (InterruptedException e) {
throw new IOException("Interrupted while waiting for verifier to finish", e);
}
return checkIfSafe(outLines.getData());
}
private String getQuery(long bound) {
return "A[] (M0.E imply t<="+bound+")";
}
private void writeQueryFile(File queryFile, long bound) throws IOException {
FileWriter fw = new FileWriter(queryFile);
fw.write(getQuery(bound));
fw.write('\n');
fw.close();
}
private boolean checkIfSafe(List<String> vector) throws IOException {
if(vector.size() == 0) {
throw new IOException("No output from verifyta");
}
String last = vector.get(vector.size()-1);
if(last.matches(".*NOT satisfied.*") ||
last.matches(".*MAYBE satisfied.*")) {
return false;
} else if(last.matches(".*Property is satisfied.*")) {
return true;
} else {
throw new IOException("Unexpected output from verifyta: "+last);
}
}
public static String getVerifytaVersion(String vbinary) throws IOException {
String[] cmd = { vbinary, "-v" };
Process verifier = Runtime.getRuntime().exec(cmd);
StreamReaderThread outLines = new StreamReaderThread(verifier.getInputStream());
outLines.run();
StreamReaderThread errLines = new StreamReaderThread(verifier.getErrorStream());
errLines.run();
try {
if(verifier.waitFor() != 0) {
throw new IOException("Uppaal verifier terminated with exit code: "+verifier.exitValue());
} else {
return outLines.getData().iterator().next();
}
} catch (InterruptedException e) {
throw new IOException("Interrupted while waiting for verifier to finish", e);
}
}
}