package gov.nasa.jpf.listener; import cmu.conditional.Conditional; import gov.nasa.jpf.Config; import gov.nasa.jpf.JPF; import gov.nasa.jpf.ListenerAdapter; import gov.nasa.jpf.report.ConsolePublisher; import gov.nasa.jpf.report.Publisher; import gov.nasa.jpf.report.PublisherExtension; import gov.nasa.jpf.search.Search; import gov.nasa.jpf.vm.ChoiceGenerator; import gov.nasa.jpf.vm.SystemState; import gov.nasa.jpf.vm.VM; import java.io.FileNotFoundException; import java.io.PrintWriter; /** * generic choice tracker tool, to produce a list of choice values that * can be used to create readable replay scripts etc. */ public class ChoiceTracker extends ListenerAdapter implements PublisherExtension { enum Format { CG, CHOICE }; Config config; VM vm; Search search; protected PrintWriter pw; Class<?>[] cgClasses; boolean isReportExtension; boolean showLocation; Format format = Format.CHOICE; String[] excludes; // <2do> hardwired type specific tracker for use with some shells - check if // we can get rid of it public ChoiceTracker (JPF jpf, String traceFileName, Class<?> cgClass){ config = jpf.getConfig(); vm = jpf.getVM(); search = jpf.getSearch(); cgClasses = new Class<?>[1]; cgClasses[0] = cgClass; try { pw = new PrintWriter(traceFileName); } catch (FileNotFoundException fnfx) { System.err.println("cannot write choice trace to file: " + traceFileName); pw = new PrintWriter(System.out); } } public ChoiceTracker (Config config, JPF jpf) { this.config = config; vm = jpf.getVM(); search = jpf.getSearch(); String fname = config.getString("choice.trace"); if (fname == null) { isReportExtension = true; jpf.addPublisherExtension(ConsolePublisher.class, this); // pw is going to be set later } else { try { pw = new PrintWriter(fname); } catch (FileNotFoundException fnfx) { System.err.println("cannot write choice trace to file: " + fname); pw = new PrintWriter(System.out); } } excludes = config.getStringArray("choice.exclude"); cgClasses = config.getClasses("choice.class"); format = config.getEnum("choice.format", Format.values(), Format.CG); showLocation = config.getBoolean("choice.show_location", true); } public void setExcludes (String... ex) { excludes=ex; } boolean isRelevantCG (ChoiceGenerator<?> cg){ if (cgClasses == null){ return true; } else { for (Class<?> cls : cgClasses){ if (cls.isAssignableFrom(cg.getClass())){ return true; } } return false; } } @Override public void propertyViolated (Search search) { if (!isReportExtension) { pw.print("// application: "); pw.println( search.getVM().getSUTDescription()); if (cgClasses == null) { pw.println("// trace over all CG classes"); } else { pw.print("// trace over CG types: "); for (Class<?> cls : cgClasses){ pw.print(cls.getName()); pw.print(' '); } pw.println(); } pw.println("//------------------------- choice trace"); printChoices(); pw.println("//------------------------- end choice trace"); pw.flush(); } } void printChoices () { int i = 0; SystemState ss = vm.getSystemState(); ChoiceGenerator<?>[] cgStack = ss.getChoiceGenerators(); nextChoice: for (ChoiceGenerator<?> cg : cgStack) { if (isRelevantCG(cg) && !cg.isDone()){ Object choice = cg.getNextChoice(); if (choice == null) { continue; } else { if (excludes != null) { for (String e : excludes) { if (choice.toString().startsWith(e)) { continue nextChoice; } } } } String line = null; switch (format){ case CHOICE: line = choice.toString(); if (line.startsWith("gov.nasa.jpf.vm.")){ line = line.substring(17); } break; case CG: line = cg.toString(); if (line.startsWith("gov.nasa.jpf.vm.choice.")){ line = line.substring(24); } break; } if (line != null){ pw.print(String.format("%4d: ", i++)); pw.print(line); if (showLocation) { Conditional<String> loc = cg.getSourceLocation(); if (loc != null) { pw.println(); pw.print(" \tat "); pw.print(loc); } } pw.println(); } } } } //--- the PublisherExtension interface @Override public void publishPropertyViolation (Publisher publisher) { pw = publisher.getOut(); publisher.publishTopicStart("choice trace " + publisher.getLastErrorId()); printChoices(); } }