package janala.solvers; import janala.Main; import janala.config.Config; import janala.interpreters.*; import janala.utils.FileUtil; import janala.utils.MyLogger; import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.LinkedList; import java.util.Stack; import java.util.logging.Level; import java.util.logging.Logger; /** * A collection of import check points (element) and the corresponding constraints */ public class History { private final static Logger logger = MyLogger.getLogger(History.class.getName()); private final static Logger tester = MyLogger.getTestLogger(Config.mainClass + "." + Config.iteration); private List<Element> history; // A list of branches or scope begin/end public List<Element> getHistory() { return history; } private List<Constraint> pathConstraint; // A list of nonempty constraints. public List<Constraint> getPathConstraint() { return pathConstraint; } private int index; // Always point to the next entry in the current path. public void setIndex(int index) { this.index = index; } public int getIndex() { return index; } private final Solver solver; private boolean ignore; private boolean predictionFailed = false; private final Config config; private final List<InputElement> inputs; private final Strategy strategy; private final FileUtil fileUtil; public History(Solver solver, FileUtil fileUtil, Config config) { this.config = config; history = new ArrayList<Element>(1024); pathConstraint = new ArrayList<Constraint>(1024); inputs = new LinkedList<InputElement>(); index = 0; this.solver = solver; this.ignore = false; this.fileUtil = fileUtil; this.strategy = config.getStrategy(); } public SymbolicOrValue assumeOrBegin(IntValue arg) { Constraint last = this.removeLastBranch(); boolean res = arg.concrete != 0; if (!res && last != null) { last = last.not(); } return new SymbolicOrValue(res, new SymbolicOrConstraint(last)); } public SymbolicOrValue assumeOr(IntValue first, SymbolicOrValue second) { Constraint last = this.removeLastBranch(); SymbolicOrValue b2 = second; SymbolicOrConstraint tmp; boolean res = first.concrete != 0; if (!res && last != null) { last = last.not(); } tmp = b2.symbolic.OR(last); return new SymbolicOrValue(res || b2.concrete, tmp); } public Value assumeOrEnd(int iid, SymbolicOrValue b) { boolean res = b.concrete; Constraint c; if (!res) c = b.symbolic.not(); else c = b.symbolic; this.checkAndSetBranch(res, c, iid); if (b.concrete) { this.setLastBranchDone(); } return PlaceHolder.instance; } public static void createBackTrackHistory(int skipIndex, String fileName) throws Exception { History.createBacktrackHistory(skipIndex, new FileInputStream(fileName), new FileOutputStream(fileName + ".bak")); } @SuppressWarnings("unchecked") public static void createBacktrackHistory(int skipIndex, InputStream is, OutputStream os) { ObjectInputStream inputStream = null; try { inputStream = new ObjectInputStream(is); Object tmp = inputStream.readObject(); if (tmp instanceof ArrayList) { ArrayList<Element> history = (ArrayList<Element>) tmp; ((BranchElement) history.get(skipIndex)).setDone(true); ObjectOutputStream outputStream; outputStream = new ObjectOutputStream(os); outputStream.writeObject(history); outputStream.close(); } else { logger.log(Level.SEVERE, "History is not in the right format!"); } } catch (Exception e) { logger.log(Level.SEVERE, "", e); e.printStackTrace(); } finally { try { if (inputStream != null) { inputStream.close(); } } catch (IOException ex) { logger.log(Level.SEVERE, "", ex); } } } /** Read history from the history file in the configuration. */ public static History readHistory(Solver solver) { try { return readHistory(solver, new FileInputStream(Config.instance.history)); } catch (Exception ex) { logger.log(Level.WARNING, "", ex); return new History(solver, new FileUtil(), Config.instance); } } @SuppressWarnings("unchecked") public static History readHistory(Solver solver, InputStream is) { History ret = new History(solver, new FileUtil(), Config.instance); try { ObjectInputStream inputStream = new ObjectInputStream(is); try { Object tmp = inputStream.readObject(); if (tmp instanceof ArrayList) { ret.history = (ArrayList<Element>) tmp; } } catch (Exception ex) { logger.log(Level.WARNING, "", ex); inputStream.close(); } } catch (IOException ex) { logger.log(Level.WARNING, "", ex); } ret.print(); return ret; } public void print() { int i = 0; if (config.printHistory) { System.out.println("History"); System.out.println("-------"); for (Element e : history) { System.out.println(i + ":" + e); i++; } } } private static boolean isEnd(Element tmp) { return tmp instanceof MethodElement && !((MethodElement) tmp).isBegin; } Stack<MethodElement> scopeStack = new Stack<MethodElement>(); MethodElement lastScope; private int skip = 0; public int getSkip() { return skip; } private void setInPrefix() { if (index < history.size()) { Element tmp = history.get(index); if (isEnd(tmp)) { Main.isInPrefix = false; } else { Main.isInPrefix = true; } } else { Main.isInPrefix = false; } } public void beginScope(int iid) { MethodElement current; if (index < history.size()) { Element tmp = history.get(index); if (isEnd(tmp)) { current = new MethodElement(true, iid); history.add(index, current); skip++; } else if (!ignore && (!(tmp instanceof MethodElement) || !((MethodElement) tmp).isBegin)) { predictionFailed = true; tester.log(Level.INFO, "Prediction failed"); logger.log( Level.WARNING, "!!!!!!!!!!!!!!!!! Prediction failed !!!!!!!!!!!!!!!!! index " + index + " history.size() " + history.size()); logger.log(Level.WARNING, "At old iid " + tmp.getIid() + " at iid " + iid + " beginScope"); current = new MethodElement(true, iid); clearAndSet(current); } else { current = (MethodElement) tmp; current.isValidExpansion = true; } } else { current = new MethodElement(true, iid); history.add(current); } scopeStack.push(current); index++; setInPrefix(); } public void endScope(int iid) { MethodElement current; if (index < history.size()) { Element tmp = history.get(index); if (isEnd(tmp) && skip > 0) { current = new MethodElement(false, iid); history.add(index, current); skip--; } else if (!ignore && (!(tmp instanceof MethodElement) || ((MethodElement) tmp).isBegin)) { predictionFailed = true; tester.log(Level.INFO, "Prediction failed"); logger.log( Level.WARNING, "!!!!!!!!!!!!!!!!! Prediction failed !!!!!!!!!!!!!!!!! index " + index + " history.size() " + history.size()); logger.log(Level.WARNING, "At old iid " + tmp.getIid() + " at iid " + iid + " endScope"); current = new MethodElement(false, iid); clearAndSet(current); } else { current = (MethodElement) tmp; } } else { current = new MethodElement(false, iid); history.add(current); } lastScope = scopeStack.pop(); index++; setInPrefix(); } /** Called from Main.AbstractEqualrConcrete */ public void abstractData(boolean isEqual) { lastScope.isValidExpansion = lastScope.isValidExpansion && isEqual; } /** Remove elements after index and set the element at index. */ private void clearAndSet(Element e) { int len = history.size(); for (int j = len - 1; j >= index; j--) { history.remove(j); } history.add(e); } public void checkAndSetBranch(boolean result, Constraint constraint, int iid) { BranchElement current; if (index < history.size()) { Element tmp = history.get(index); if (isEnd(tmp) || ignore) { current = new BranchElement(result, false, -1, iid); history.add(index, current); } else if (!ignore && (!(tmp instanceof BranchElement) || ((BranchElement) tmp).getBranch() != result)) { predictionFailed = true; tester.log(Level.INFO, "Prediction failed " + ignore); logger.log( Level.WARNING, "!!!!!!!!!!!!!!!!! Prediction failed (checkAndSetBranch) !!!!!!!!!!!!!!!!! index " + index + " history.size() " + history.size()); logger.log( Level.WARNING, "At old iid " + tmp.getIid() + " at iid " + iid + " constraint " + constraint); current = new BranchElement(result, false, -1, iid); clearAndSet(current); } else { current = (BranchElement) tmp; } } else { current = new BranchElement(result, false, -1, iid); history.add(current); } if (constraint != null) { constraint.iid = iid; constraint.index = index; pathConstraint.add(constraint); current.pathConstraintIndex = pathConstraint.size() - 1; } else { current.pathConstraintIndex = -1; } if (ignore) { ignore = false; } index++; setInPrefix(); } public void solveAndSave() { int i = 0; if (config.printConstraints) { for (Constraint c : pathConstraint) { System.out.println(i + ":" + c); i++; } } print(); String backtrackFile = "backtrackFlag"; if (predictionFailed) { // backtrack fileUtil.moveFile(config.inputs + ".bak", config.inputs); fileUtil.moveFile(config.history + ".bak", config.history); fileUtil.touch(backtrackFile); } else { if (strategy != null) { if ((i = strategy.solve(history, index, this)) >= 0) { if (fileUtil.exists(backtrackFile)) { if ((i = strategy.solve(history, history.size(), this)) >= 0) { writeHistory(i); } else { removeHistory(); } fileUtil.remove(backtrackFile); } else { writeHistory(i); } } else { removeHistory(); } } } } /** Solve the path constraint using index in pathConstraints. */ public boolean solveAt(int pathConstraintIndex) { solver.setInputs(inputs); solver.setPathConstraint(pathConstraint); solver.setPathConstraintIndex(pathConstraintIndex); for (int i = pathConstraintIndex; i >= 0; i--) { pathConstraint.get(i).accept(solver); } boolean solved = solver.solve(); if (solved) { writeInputFile(); } return solved; } /** * Solve the constraints between two indices in history. * * NOTE, the index is confusing. head is exclusive, n is inclusive */ public boolean solveAt(int head, int n) { ArrayList<Constraint> pathConstraint = collectPathConstraints(head, n); solver.setInputs(inputs); solver.setPathConstraint(pathConstraint); solver.setPathConstraintIndex(pathConstraint.size() - 1); for (int i = pathConstraint.size() - 1; i >= 0; i--) { pathConstraint.get(i).accept(solver); } boolean solved = solver.solve(); if (solved) { writeInputFile(); } return solved; } private void writeInputFile() { fileUtil.moveFile(config.inputs, config.inputs + ".bak"); try { fileUtil.write(config.inputs, solver.getSolution()); } catch (IOException e) { throw new RuntimeException(e); } } private ArrayList<Constraint> collectPathConstraints(int head, int n) { ArrayList<Constraint> ret = new ArrayList<Constraint>(); for (int i = 0; i <= head; i++) { Element tmp = history.get(i); if (tmp instanceof BranchElement) { BranchElement current = (BranchElement) tmp; if (current.pathConstraintIndex != -1) { ret.add(pathConstraint.get(current.pathConstraintIndex)); } } } int scopeLevel = 0; // scope level for (int i = head + 1; i <= n; i++) { Element tmp = history.get(i); if (tmp instanceof BranchElement) { BranchElement current = (BranchElement) tmp; if (scopeLevel == 0 && current.pathConstraintIndex != -1) { ret.add(pathConstraint.get(current.pathConstraintIndex)); } } else if (tmp instanceof MethodElement) { MethodElement melem = (MethodElement) tmp; if (melem.isBegin) { scopeLevel++; } else { scopeLevel--; } } } return ret; } private void removeHistory() { File f = new File(config.history); f.delete(); logger.log(Level.INFO, "Done with search."); } public void cleanup(int i) { if (i != Integer.MAX_VALUE) { BranchElement current = (BranchElement) history.get(i); // Set the last branch to done. current.setDone (true); current.setBranch(!current.getBranch()); int len = history.size(); for (int j = len - 1; j > i; j--) { history.remove(j); } } } private void writeHistory(int i) { cleanup(i); fileUtil.moveFile(config.history, config.history + ".bak"); try { OutputStream ostream = new FileOutputStream(config.history); writeHistory(ostream); } catch (IOException ex) { logger.log(Level.SEVERE, "", ex); } } public void writeHistory(OutputStream os) { ObjectOutputStream outputStream; try { outputStream = new ObjectOutputStream(os); outputStream.writeObject(history); outputStream.close(); } catch (IOException e) { logger.log(Level.SEVERE, "", e); throw new RuntimeException("fail"); } } public Constraint removeLastBranch() { index--; BranchElement current = (BranchElement) history.get(index); Constraint ret = null; if (current.pathConstraintIndex != -1) { assert current.pathConstraintIndex == (pathConstraint.size() -1); ret = pathConstraint.remove(pathConstraint.size() - 1); } history.remove(index); return ret; } public Value ignore() { ignore = true; return PlaceHolder.instance; } public void addInput(int symbol, Value value) { inputs.add(new InputElement(symbol, value)); } public void setLastBranchDone() { if (index >= 1 && index - 1 < history.size()) { ((BranchElement) history.get(index - 1)).setDone(true); } } public void setLastForceTruth() { if (index >= 1 && index - 1 < history.size()) { ((BranchElement) history.get(index - 1)).isForceTruth = true; } } }