/** * */ package bixie.checker.faultlocalization; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import util.Log; import bixie.checker.reportprinter.SourceLocation; import bixie.checker.transition_relation.AbstractTransitionRelation; import bixie.checker.transition_relation.FaultLocalizationTransitionRelation; import bixie.prover.Prover; import bixie.prover.ProverExpr; import bixie.prover.ProverFactory; import bixie.prover.ProverResult; import bixie.transformation.SingleStaticAssignment; import bixie.util.aspects.Loggable; import boogie.ProgramFactory; import boogie.ast.Attribute; import boogie.ast.NamedAttribute; import boogie.controlflow.BasicBlock; import boogie.controlflow.CfgAxiom; import boogie.controlflow.CfgProcedure; import boogie.controlflow.statement.CfgStatement; /** * @author schaef * */ public class FaultLocalizationThread implements Runnable { private List<Map<CfgStatement, SourceLocation>> reports = new LinkedList<Map<CfgStatement, SourceLocation>>(); private Set<BasicBlock> infeasibleBlocks; private AbstractTransitionRelation tr; private Prover prover = null; public FaultLocalizationThread(AbstractTransitionRelation tr, Set<BasicBlock> infeasibleBlocks) { this.infeasibleBlocks = infeasibleBlocks; this.tr = tr; } public List<Map<CfgStatement, SourceLocation>> getReports() { return this.reports; } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ @Override public void run() { if (this.tr != null && this.infeasibleBlocks != null) { this.reports = localizeFaults(tr, infeasibleBlocks); this.tr = null; this.infeasibleBlocks = null; shutDownProver(); } } public void shutDownProver() { if (this.prover != null) { this.prover.stop(); this.prover.setConstructProofs(false); prover.shutdown(); prover = null; } } /** * Applies fault localization to a set of (not necessarily connected) * infeasible blocks. * * @param tr * @param infeasibleBlocks */ @Loggable private List<Map<CfgStatement, SourceLocation>> localizeFaults( AbstractTransitionRelation tr, Set<BasicBlock> infeasibleBlocks) { List<Map<CfgStatement, SourceLocation>> reports = new LinkedList<Map<CfgStatement, SourceLocation>>(); LinkedList<HashSet<BasicBlock>> components = findConnectedComponents(infeasibleBlocks); ProverFactory pf = new bixie.prover.princess.PrincessProverFactory(); for (HashSet<BasicBlock> cmp : components) { try { this.prover = pf.spawn(); this.prover.setConstructProofs(true); HashMap<CfgStatement, SourceLocation> res = localizeFault(tr, cmp, this.prover); if (res != null && !res.isEmpty()) { reports.add(res); } } catch (Throwable e) { e.printStackTrace(); // throw e; } finally { shutDownProver(); } } return reports; } /** * Applies fault localization to a set of connected infeasible blocks * * @param tr * @param component */ private HashMap<CfgStatement, SourceLocation> localizeFault( AbstractTransitionRelation tr, Set<BasicBlock> component, Prover prover) { // TODO: check if this contains a noverify tag and ignore it. for (BasicBlock b : component) { if (containsNamedAttribute(b, ProgramFactory.NoVerifyTag)) { return new HashMap<CfgStatement, SourceLocation>(); } } // System.err.println("compute slice"); CfgProcedure slice = tr.getProcedure().computeSlice( getSubprog(component), tr.getProcedure().getRootNode()); if (slice.getRootNode() == null) { Log.error("Cannot construct slice for " + tr.getProcedureName()); // tr.getProcedure().toDot("DEBUG_"+tr.getProcedureName()+".dot"); // tr.getProcedure().toFile("DEBUG_"+tr.getProcedureName()+".bpl"); return new HashMap<CfgStatement, SourceLocation>(); } // System.err.println("compute ssa"); // TODO do I have to recompute SSA? SingleStaticAssignment ssa = new SingleStaticAssignment(); ssa.updateBlockSSA(slice); // slice.pruneUnreachableBlocks(); // tr.getProcedure().toDot("./orig_"+slice.getProcedureName()+".dot"); // slice.toDot("./slice_" + slice.getProcedureName() + ".dot"); // System.err.println("compute tr"); FaultLocalizationTransitionRelation sliceTr = new FaultLocalizationTransitionRelation( slice, tr.getControlFlowFactory(), prover); // System.err.println("1"); // prover.addAssertion(sliceTr.getEnsures()); // for (Entry<CfgAxiom, ProverExpr> entry : sliceTr.getPreludeAxioms() .entrySet()) { prover.addAssertion(entry.getValue()); } // System.err.println("2"); if (sliceTr.getRequires() != null) prover.addAssertion(sliceTr.getRequires()); int partition = 0; prover.setPartitionNumber(partition); // System.err.println("obligations "+ sliceTr.obligations.size()); for (ProverExpr assertion : sliceTr.obligations) { prover.addAssertion(assertion); prover.setPartitionNumber(++partition); } if (sliceTr.getEnsures() != null) { prover.addAssertion(sliceTr.getEnsures()); } prover.setPartitionNumber(++partition); // System.err.println("check sat"); ProverResult res = prover.checkSat(true); if (res != ProverResult.Unsat) { throw new RuntimeException("Fault Localization failed! " + res.toString() + " in proc "+tr.getProcedureName()); } int[][] ordering = new int[partition][1]; for (int i = 0; i < partition; i++) { ordering[i][0] = i; } // System.err.println("compute interpolants"); ProverExpr[] interpolants = prover.interpolate(ordering); boolean allInfeasibleCloned = true; boolean anyCloned = false; SourceLocation maxLoc = null; // System.err.println("compute abstract slice"); // System.err.println("\t\t **"); HashMap<CfgStatement, SourceLocation> interestingStatements = new HashMap<CfgStatement, SourceLocation>(); ProverExpr currentInterpolant = interpolants[0]; for (int i = 1; i < interpolants.length; i++) { if (!interpolants[i].equals(currentInterpolant)) { currentInterpolant = interpolants[i]; CfgStatement statement = sliceTr.pe2StmtMap .get(sliceTr.obligations.get(i)); if (statement == null) { // TODO: statement = sliceTr.pe2StmtMap.get(sliceTr.obligations .get(i - 1)); } BasicBlock origin = sliceTr.stmtOriginMap.get(statement); if (origin == null) { Log.debug("could not find statement " + statement + " in " + sliceTr.getProcedureName() + "\n"); continue; } // if (origin==null || containsNamedAttribute(origin, // ProgramFactory.Cloned)) { // continue; // } SourceLocation loc = null; if (containsNamedAttribute(statement, ProgramFactory.GeneratedThenBlock) || containsNamedAttribute(statement, ProgramFactory.GeneratedElseBlock)) { int pos = origin.getStatements().indexOf(statement); while (pos < origin.getStatements().size()) { CfgStatement st = origin.getStatements().get(pos); // System.err.println("\t"+st); loc = praseLocationTags(st.getAttributes()); if (loc != null) { break; } pos++; } // System.err.println("FWD"); } else { // if its a statement without location attributes // go backwards until we find the last location attibute. int pos = origin.getStatements().indexOf(statement); while (pos >= 0) { CfgStatement st = origin.getStatements().get(pos); loc = praseLocationTags(st.getAttributes()); if (loc != null) { break; } pos--; } if (loc == null) { // then try it in the other direction. pos = origin.getStatements().indexOf(statement); while (pos < origin.getStatements().size()) { CfgStatement st = origin.getStatements().get(pos); // System.err.println("\t"+st); loc = praseLocationTags(st.getAttributes()); if (loc != null) { break; } pos++; } } // System.err.println("BWD"); } if (loc != null) { if (containsNamedAttribute(origin, ProgramFactory.Cloned)) { loc.isCloned = true; anyCloned = true; } if (containsNamedAttribute(origin, ProgramFactory.NoVerifyTag)) { loc.isNoVerify = true; } for (BasicBlock b : component) { if (b.getLabel().equals(origin.getLabel())) { // compare them by name because we cloned them, // so // they are not // the same by reference. loc.inInfeasibleBlock = true; break; } } if (loc.inInfeasibleBlock && !loc.isCloned) { allInfeasibleCloned = false; } if (maxLoc == null) { maxLoc = loc; } else { if (loc.StartLine > maxLoc.StartLine) { maxLoc = loc; } else if (loc.StartLine == maxLoc.StartLine) { maxLoc.isNoVerify = maxLoc.isNoVerify || loc.isNoVerify; } } interestingStatements.put(statement, loc); } else { Log.debug("no location tag " + statement + " in "); Log.debug(origin); continue; } // System.err.println("\t" + statement); // System.err.println("with interpolant: " + interpolants[i]); } } // System.err.println("============"); // System.err.println("done"); // TODO: if (anyCloned && allInfeasibleCloned) interestingStatements.clear(); if (maxLoc != null && maxLoc.isNoVerify) { // TODO: this is sort of a hack that helps to suppress // strange try/catch false positives. interestingStatements.clear(); } return interestingStatements; } private SourceLocation praseLocationTags(Attribute[] attributes) { if (attributes == null) return null; return SourceLocation.readSourceLocationFromAttributes(attributes); } private boolean containsNamedAttribute(BasicBlock b, String name) { for (CfgStatement s : b.getStatements()) { if (containsNamedAttribute(s, name)) return true; } return false; } private boolean containsNamedAttribute(CfgStatement s, String name) { if (s.getAttributes() != null) { for (Attribute attr : s.getAttributes()) { if (attr instanceof NamedAttribute) { NamedAttribute na = (NamedAttribute) attr; if (na.getName().equals(name)) { return true; } } } } return false; } /** * Collect all blocks that must can reach or can be reached by the elements * in component * * @param component * @return */ private Set<BasicBlock> getSubprog(Set<BasicBlock> component) { HashSet<BasicBlock> result = new HashSet<BasicBlock>(); result.addAll(getNeighbors(component, true)); result.addAll(getNeighbors(component, false)); return result; } private Set<BasicBlock> getNeighbors(Set<BasicBlock> blocks, boolean forward) { LinkedList<BasicBlock> todo = new LinkedList<BasicBlock>(); HashSet<BasicBlock> done = new HashSet<BasicBlock>(); todo.addAll(blocks); while (!todo.isEmpty()) { BasicBlock current = todo.pop(); done.add(current); for (BasicBlock b : getNeighbors(current, forward)) { if (!todo.contains(b) && !done.contains(b)) { todo.add(b); } } } return done; } private Set<BasicBlock> getNeighbors(BasicBlock b, boolean forward) { if (forward) return b.getSuccessors(); return b.getPredecessors(); } private LinkedList<HashSet<BasicBlock>> findConnectedComponents( Set<BasicBlock> blocks) { LinkedList<HashSet<BasicBlock>> components = new LinkedList<HashSet<BasicBlock>>(); LinkedList<BasicBlock> allblocks = new LinkedList<BasicBlock>(); allblocks.addAll(blocks); while (!allblocks.isEmpty()) { HashSet<BasicBlock> subprog = new HashSet<BasicBlock>(); LinkedList<BasicBlock> todo = new LinkedList<BasicBlock>(); todo.add(allblocks.pop()); while (!todo.isEmpty()) { BasicBlock current = todo.pop(); allblocks.remove(current); subprog.add(current); for (BasicBlock b : current.getPredecessors()) { if (!subprog.contains(b) && !todo.contains(b) && allblocks.contains(b)) { todo.add(b); } } for (BasicBlock b : current.getSuccessors()) { if (!subprog.contains(b) && !todo.contains(b) && allblocks.contains(b)) { todo.add(b); } } } if (subprog.size() > 0) { components.add(subprog); } } return components; } }