/** * This does the work of main, but it never calls System.exit, so it is appropriate to be called * progrmmatically. Termination of the program with a message to the user is indicated by throwing * Daikon.TerminationMessage. * * @see #main(String[]) * @see daikon.Daikon.TerminationMessage */ public static void mainHelper(final String[] args) throws FileNotFoundException, IOException, ClassNotFoundException { daikon.LogHelper.setupLogs(daikon.LogHelper.INFO); LongOpt[] longopts = new LongOpt[] { new LongOpt(Daikon.suppress_redundant_SWITCH, LongOpt.NO_ARGUMENT, null, 0), new LongOpt(Daikon.config_option_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0), new LongOpt(Daikon.debugAll_SWITCH, LongOpt.NO_ARGUMENT, null, 0), new LongOpt(Daikon.debug_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0), }; Getopt g = new Getopt("daikon.ExtractConsequent", args, "h", longopts); int c; while ((c = g.getopt()) != -1) { switch (c) { case 0: // got a long option String option_name = longopts[g.getLongind()].getName(); if (Daikon.help_SWITCH.equals(option_name)) { System.out.println(usage); throw new Daikon.TerminationMessage(); } else if (Daikon.suppress_redundant_SWITCH.equals(option_name)) { Daikon.suppress_redundant_invariants_with_simplify = true; } else if (Daikon.config_option_SWITCH.equals(option_name)) { String item = Daikon.getOptarg(g); daikon.config.Configuration.getInstance().apply(item); break; } else if (Daikon.debugAll_SWITCH.equals(option_name)) { Global.debugAll = true; } else if (Daikon.debug_SWITCH.equals(option_name)) { LogHelper.setLevel(Daikon.getOptarg(g), LogHelper.FINE); } else { throw new RuntimeException("Unknown long option received: " + option_name); } break; case 'h': System.out.println(usage); throw new Daikon.TerminationMessage(); case '?': break; // getopt() already printed an error default: System.out.println("getopt() returned " + c); break; } } // The index of the first non-option argument -- the name of the file int fileIndex = g.getOptind(); if (args.length - fileIndex != 1) { throw new Daikon.TerminationMessage("Wrong number of arguments." + Daikon.lineSep + usage); } String filename = args[fileIndex]; PptMap ppts = FileIO.read_serialized_pptmap( new File(filename), true // use saved config ); extract_consequent(ppts); }
/** * This is a tester for the results of adding or checking an sample to an invariant. It can test * practically any invariant in the Daikon system given the appropriate commands. The test are * configured from the InvariantTest.commands file and errors that occur are written to the * InvariantTest.diffs file. For conveince a partcailly complete file InvariantTest.input can be * used to generate a complete commands file. To generate InvariantTest.commands from * InvariantTest.input run this class's main method with option "--generate_goals". * * <p>Each test case starts with a line containing the full name of the invariant then a second line * containing the types of the arguments to the invariant's check and add method (excluding the * count argument). After the test case's header any number of command lines may be included in a * command file. Finally, after all command lines, the key work "end" must appear on its own line. * * <p>Each command line is starts with a command: "add:" or "check:". Following the command comes * the arguments to be checked or added to the invariant. These arguments should be in the same * format as in a dtrace file. Next comes the the InvariantStatus that is expected to be returned by * the check or add command on checking or adding the arguments. Finally, the expected format of the * Invariant after checking or adding the arguments is included. (The format of the invariant is * given by "Invariant.format_using(OutputFormat.DAIKON)") * * <p>A full example test case is as follows: * * <p>daikon.inv.binary.twoSequence.PairwiseIntEqual int_array int_array add: [ 1 2 3 ]; [1 2 3 ]; * no_change; a[] == b[] (elementwise) add: [ 10 -1 6 ]; [10 -1 6 ]; no_change; a[] == b[] * (elementwise) add: [ -3 -3 -9 ]; [-3 6 -9 ]; falsified; a[] == b[] (elementwise) end * * <p>Alternatively, this class can be used to generate a test case when given command lines that do * not include expected InvariantStatus or invariant format. The proper input to generate the test * above would be: * * <p>daikon.inv.binary.twoSequence.PairwiseIntEqual int_array int_array add: [ 1 2 3 ]; [1 2 3 ] * add: [ 10 -1 6 ]; [10 -1 6 ] add: [ -3 -3 -9 ]; [-3 6 -9 ] end * * <p>To run a test case the method runTest should be used. To generate a test case the method * generateTest should be used. * * <p>Note: no test case should contain the character ';' in any way other than to divide arguments * with in a command line. */ public class InvariantAddAndCheckTester extends TestCase { /** * Maximum file size that can currently be examined by the program. It is arbitrary, but a length * must be supplied to LineNumberReader.mark(). */ private static final int MAX_FILE_SIZE = 262144; /** Indicates a string that when it starts a line signifies that the line is a comment. */ public static final String COMMENT_STARTER_STRING = "#"; /** A list containing all of the test formats. */ public static final List<String> TEST_FORMAT_LIST = getTestFormatList(); /** Allows for the configuring of Daikon options. */ static Configuration config = Configuration.getInstance(); private static final String inputFileName = "daikon/test/inv/InvariantTest.input"; private static final String commandsFileName = "daikon/test/inv/InvariantTest.commands"; private static final String diffFileName = "daikon/test/inv/InvariantTest.diffs"; private static final String lineSep = Global.lineSep; /** * This function allows this test to be run from the command line instead of its usual method, * which is through the Daikon MasterTester. * * @param args arguments to the main function, which control options to the program. As of now * there is only one option, "--generate_goals", which will generate goal information for the * selected tests assuming the output that the tests provide is the correct output */ public static void main(String[] args) { daikon.LogHelper.setupLogs(daikon.LogHelper.INFO); if (args.length == 1 && args[0].equalsIgnoreCase("--generate_goals")) { writeCommandFile(); } else if (args.length > 0) { throw new Daikon.TerminationMessage( "Usage: java daikon.test.InvariantAddAndCheckTester" + " [--generate_goals]"); } else { junit.textui.TestRunner.run(new TestSuite(InvariantAddAndCheckTester.class)); } } /** * This constructor allows the test to be created from the MasterTester class. * * @param name the desired name of the test case */ public InvariantAddAndCheckTester(String name) { super(name); } /** * This function produces the format list for intialization of the static format list variable. */ static List<String> getTestFormatList() { List<String> result = new Vector<String>(); // Add test formats - hard coded in result.add("daikon"); result.add("java"); result.add("esc"); result.add("jml"); result.add("dbc"); result.add("simplify"); return result; } /** This function is the actual function performed when this class is run through JUnit. */ public static void testFormats() { // Don't care about comparability info because we are only // creating variables for the purpose of being compared (thus they // should all be comparable) Daikon.ignore_comparability = true; // run the actual test if (!execute()) { fail( "At least one test failed." + " Inspect java/daikon/test/InvariantAddAndCheckTest.diffs for error report."); } } /** * Returns the next non-comment, non-whitespace line of the input buffer. * * @param input the input buffer * @return the next non-comment, non-whitespace line of the input buffer or null if the end of the * buffer is reached before such a line can be found */ static /*@Nullable*/ String getNextRealLine(BufferedReader input) { String currentLine = ""; try { while (currentLine != null) { currentLine = input.readLine(); if (currentLine != null && !isComment(currentLine) && !isWhitespace(currentLine)) return currentLine; } } catch (IOException e) { throw new RuntimeException(e.toString()); } return null; } /** * This function performs the testing for a particular format indicated by the format string. It * subsequently sets up appropriate input and output streams for the format test, performs the * test, and the compares the test results to the goals. If the goals differ from the actual * results the test fails. * * @return false if any tests fail. */ private static boolean execute() { LineNumberReader commandReader = getCommands(); String output = performTest(commandReader); if (output == null) { // no errors return true; } else { FileWriter diffsOutput = getDiffsOutputWriter(); try { diffsOutput.write(output, 0, output.length()); diffsOutput.close(); } catch (IOException e) { throw new RuntimeException("Could not output generated diffs"); } return false; } } private static void writeCommandFile() { LineNumberReader inputReader = getInputReader(); String output = generateCommands(inputReader); FileWriter commandOutput = getCommandWriter(); try { commandOutput.write(output, 0, output.length()); commandOutput.close(); } catch (IOException e) { throw new RuntimeException("Could not output generated commands"); } System.out.println("Goals generated"); } /** * This function performs an individual formatting test after the input and output streams have * been created. * * @param commands the input that decides which tests to perform * @return a String holding the error messages for any failed tests or null if no tests are * failed. */ private static /*@Nullable*/ String performTest(LineNumberReader commands) { StringBuffer output = new StringBuffer(); // List invariantTestCases = new Vector(); boolean noTestFailed = true; while (true) { // Create a new test case // FormatTestCase currentCase = FormatTestCase.instantiate(commands, generateGoals); // if (currentCase == null) // break; // else { // invariantTestCases.add(currentCase); String results = AddAndCheckTestCase.runTest(commands); if (results == null) break; if (!(results.length() == 0)) { // output.print(currentCase.getDiffString()); output.append(results); noTestFailed = false; } } if (noTestFailed) { return null; } else { return output.toString(); } } private static String generateCommands(LineNumberReader input) { StringBuffer output = new StringBuffer(); while (true) { String commands = AddAndCheckTestCase.generateTest(input); if (commands == null) break; output.append(commands); } return output.toString(); } private static LineNumberReader getInputReader() { // Calculate input file locations // URL inputFileLocation = // ClassLoader.getSystemClassLoader().getSystemResource("InvariantTest.commands"); // if (inputFileLocation == null) // fail("Input file for invariant format tests missing." + // " (Should be in InvariantTest.commands" + // " and it must be within the classpath)"); // String inputFile = inputFileLocation.getFile(); LineNumberReader input = null; try { input = new LineNumberReader(new InputStreamReader(new FileInputStream(inputFileName))); } catch (FileNotFoundException e) { fail( "Unexpected FileNotFoundException (very strange since the URL" + " of the file was found earlier)"); } return input; } private static FileWriter getCommandWriter() { try { return new FileWriter(commandsFileName); } catch (IOException e) { throw new RuntimeException("Cannot write output into " + commandsFileName); } } private static LineNumberReader getCommands() { // Calculate input file locations // URL inputFileLocation = // ClassLoader.getSystemClassLoader().getSystemResource("InvariantTest.commands"); // if (inputFileLocation == null) // fail("Input file for invariant format tests missing." + // " (Should be in InvariantTest.commands" + // " and it must be within the classpath)"); // String inputFile = inputFileLocation.getFile(); // System.out.println(System.getProperty("user.dir")); LineNumberReader commands = null; try { commands = new LineNumberReader(new InputStreamReader(new FileInputStream(commandsFileName))); } catch (FileNotFoundException e) { fail( "Unexpected FileNotFoundException (very strange since the URL" + " of the file was found earlier)"); } return commands; } private static FileWriter getDiffsOutputWriter() { try { return new FileWriter(new File(diffFileName)); } catch (IOException e) { throw new RuntimeException("Cannot write output into " + diffFileName); } } /** * Determines whether a line is a comment or not. * * @param line the line in question * @return true if the line is a comment (that is, not to be interpretted as a command) false * otherwise */ static boolean isComment(String line) { return line.startsWith(COMMENT_STARTER_STRING); } /** * Determines whether a given line is made only of whitespcae. * * @param line the line in question * @return true if the line is made up only of whitespace, false otherwise */ static boolean isWhitespace(String line) { for (int x = 0; x < line.length(); x++) { if (!Character.isWhitespace(line.charAt(x))) return false; } return true; } private static class AddAndCheckTestCase { /** The Invariant object to be tested. */ private static Invariant invariantToTest; /** * The types of the arguments to invariantToTest's check and add commands (not including the * count argument). */ private static ProglangType[] types; /** invariantToTest's addModified method. */ private static Method addModified; /** invariantToTest's checkModified method. */ private static Method checkModified; /** invariantToTest's format_using method. */ private static Method outputProducer; /** Contains error messages if any test commands fail. */ private static StringBuffer results; /** The token that divides the different arguments to a test command. */ private static final String argDivider = ";"; /** * @return String containing error messages for any failed cases. In the case that there are no * failed cases, the empty string is returned. In the case where commands is empty (there * are no more test cases and the end of the file has been reached), null is returned. */ public static String runTest(LineNumberReader commands) { boolean endOfFile = initFields(commands, false); if (endOfFile) { return null; } while (true) { String commandLine = getNextRealLine(commands); int lineNumber = commands.getLineNumber(); if (InvariantAddAndCheckTester.isComment(commandLine)) { continue; } else if (isTestTerminator(commandLine)) { break; } else if (isAddCommand(commandLine) || isCheckCommand(commandLine)) { exicuteCheckOrAddCommand(commandLine, lineNumber); } else if (isCompareCommand(commandLine)) { } else { throw new RuntimeException("unrecognized command"); } } return results.toString(); } /** * @return a String containing the proper add and check commands for this input lines of this * test case. */ public static String generateTest(LineNumberReader commands) { boolean endOfFile = initFields(commands, true); if (endOfFile) return null; while (true) { String commandLine = getNextLine(commands).trim(); int lineNumber = commands.getLineNumber(); if (InvariantAddAndCheckTester.isComment(commandLine)) { results.append(commandLine + lineSep); } else if (isTestTerminator(commandLine)) { results.append(commandLine + lineSep + lineSep); break; } else if (isAddCommand(commandLine) || isCheckCommand(commandLine)) { generateCheckOrAddCommand(commandLine, lineNumber); } else if (isCompareCommand(commandLine)) { // generateCompareCommand(commandLine); } else { throw new RuntimeException("unrecognized command"); } } return results.toString(); } /** * Initializes the fields of this class based on the first two lines of a case which include the * class name and parameter types. * * @return true is end of file is reached. */ private static boolean initFields(LineNumberReader commands, boolean generatingCommands) { results = new StringBuffer(); String className = getNextRealLine(commands); // End of file reached if (className == null) return true; // Load the class from file Class<? extends Invariant> classToTest = asInvClass(getClass(className)); try { classToTest.getField("dkconfig_enabled"); // Enable if needs to be done InvariantAddAndCheckTester.config.apply(className + ".enabled", "true"); } catch (NoSuchFieldException e) { // Otherwise do nothing } if (generatingCommands) { results.append(className + lineSep); } // Instantiate variables to be used as the names in the // invariants, variables are labeled a,b,c and so on as they // appear String typeString = getNextRealLine(commands); types = getTypes(typeString); VarInfo[] vars = getVarInfos(classToTest, types); PptSlice sl = createSlice(vars, daikon.test.Common.makePptTopLevel("Test:::OBJECT", vars)); // Create an actual instance of the class invariantToTest = instantiateClass(classToTest, sl); addModified = getAddModified(invariantToTest.getClass()); checkModified = getCheckModified(invariantToTest.getClass()); outputProducer = getOutputProducer(invariantToTest.getClass()); assert getArity(invariantToTest.getClass()) == types.length; if (generatingCommands) { results.append(typeString + lineSep); } return false; } /** * Given a line from a command file, generates executes the appropriate check or add command and * checks the results against the goal. If the results and goal do not match, a message is added * to the results string buffer. */ private static void exicuteCheckOrAddCommand(String command, int lineNumber) { // remove the command String args = command.substring(command.indexOf(":") + 1); StringTokenizer tokens = new StringTokenizer(args, argDivider); if (tokens.countTokens() != types.length + 2) { throw new RuntimeException( "Number of arguments to add command on line " + lineNumber + " is: " + tokens.countTokens() + " but should be: " + (types.length + 2)); } Object[] params = getParams(tokens); InvariantStatus goalStatus = parseStatus(tokens.nextToken().trim()); tokens.nextToken(); // executed for side effect assert !tokens.hasMoreTokens(); InvariantStatus resultStatus = null; if (isCheckCommand(command)) { resultStatus = getCheckStatus(params); } else { resultStatus = getAddStatus(params); } if (resultStatus != goalStatus) { results.append( "Error on line " + lineNumber + ":" + lineSep + "Expected InvariantStatus: " + goalStatus + lineSep + "Found InvariantStatus: " + resultStatus + lineSep); } } /** Given a line from an input file, generates appropriate check or add command. */ private static void generateCheckOrAddCommand(String command, int lineNumber) { // remove the command String args = command.substring(command.indexOf(":") + 1); StringTokenizer tokens = new StringTokenizer(args, argDivider); if (tokens.countTokens() != types.length) { throw new RuntimeException( "Number of arguments to generate an add command on line: " + lineNumber + " is: " + tokens.countTokens() + " but should be: " + types.length); } Object[] params = getParams(tokens); assert !tokens.hasMoreTokens(); InvariantStatus goalStatus = null; if (isCheckCommand(command)) { goalStatus = getCheckStatus(params); } else { goalStatus = getAddStatus(params); } String invariantFormat = getInvariantFormat(); results.append( command + argDivider + " " + goalStatus.toString() + argDivider + " " + invariantFormat + lineSep); } /** * @return an array of the arguments to be passed into check_modified or add_modified produced * from tokens. */ private static Object[] getParams(StringTokenizer tokens) { // add one for the "count" argument Object[] params = new Object[types.length + 1]; for (int i = 0; i < types.length; i++) { params[i] = types[i].parse_value(tokens.nextToken().trim()); } params[params.length - 1] = new Integer(1); // the "count" argument return params; } /** * @return the InvariantStatus produced by invoking invariantToTest's add_modified method on the * arguments represented by params. */ private static InvariantStatus getAddStatus(Object[] params) { try { return (InvariantStatus) addModified.invoke(invariantToTest, params); } catch (Exception e) { throw new RuntimeException(" error in " + invariantToTest.getClass() + ": " + e); } } /** * @return the InvariantStatus produced by invoking invariantToTest's check_modified method on * the arguments represented by params. */ private static InvariantStatus getCheckStatus(Object[] params) { try { return (InvariantStatus) checkModified.invoke(invariantToTest, params); } catch (Exception e) { throw new RuntimeException(" error in " + invariantToTest.getClass() + ": " + e); } } /** * @return a String representation of the invariantToTest. This String is produced by invoking * the invariant's format_using with method with the argument OutputFormat.Daikon. */ private static String getInvariantFormat() { try { return (String) outputProducer.invoke(invariantToTest, new Object[] {OutputFormat.DAIKON}); } catch (Exception e) { throw new RuntimeException(invariantToTest + " " + outputProducer); } } /** @return an InvariantStatus that the string status parses to. */ private static InvariantStatus parseStatus(String status) { status = status.trim(); if (status.equals("no_change")) { return InvariantStatus.NO_CHANGE; } else if (status.equals("falsified")) { return InvariantStatus.FALSIFIED; } if (status.equals("weakened")) { return InvariantStatus.WEAKENED; } else { throw new RuntimeException("Unrecognized InvariantStatus: " + status); } } /** * This function returns the add_modified method from the class type provided. * * @param theClass the class in which to find the add_modified method * @return the add_modified method if it exists, null otherwise * @throws RuntimeException if check_modified does not exist. */ private static Method getAddModified(Class<? extends Invariant> theClass) { Method[] methods = theClass.getMethods(); Method currentMethod; for (int i = 0; i < methods.length; i++) { currentMethod = methods[i]; if (currentMethod.getName().lastIndexOf("add_modified") != -1) { // Method should be called add_modified return currentMethod; } } throw new RuntimeException("Cannot find add_modified method"); } /** * This function returns the check_modified method from the class type provided. * * @param theClass the class in which to find the check_modified method * @return the check_modified method if it exists * @throws RuntimeException if check_modified does not exist. */ private static Method getCheckModified(Class<? extends Invariant> theClass) { Method[] methods = theClass.getMethods(); Method currentMethod; for (int i = 0; i < methods.length; i++) { currentMethod = methods[i]; if (currentMethod.getName().lastIndexOf("check_modified") != -1) { // Method should be called check_modified return currentMethod; } } throw new RuntimeException("Cannot find check_modified method"); } /** * @return the method of invariant named by theClass that produces a String representation of * the invariant. */ private static Method getOutputProducer(Class<? extends Invariant> theClass) { Method[] methods = theClass.getMethods(); Method currentMethod; for (int i = 0; i < methods.length; i++) { currentMethod = methods[i]; // Method should be called format_using if (currentMethod.getName().lastIndexOf("format_using") != -1) { return currentMethod; } } throw new RuntimeException("Cannot find format_using method"); } /** * This function loads a class from file into the JVM given its fully-qualified name. * * @param classInfo the fully-qualified class name * @return a Class object representing the class name if such a class is defined, otherwise null */ private static Class<?> getClass(String classInfo) { try { return ClassLoader.getSystemClassLoader().loadClass(classInfo); } catch (ClassNotFoundException e) { throw new RuntimeException(e.toString()); } } /** * This function is an alias for the {@link #getNextRealLine(BufferedReader) getNextRealLine} * method. */ static String getNextRealLine(BufferedReader buffer) { return InvariantAddAndCheckTester.getNextRealLine(buffer); } private static boolean isTestTerminator(String command) { String commandTrimmed = command.trim(); return commandTrimmed.startsWith("end"); } private static boolean isAddCommand(String command) { String commandTrimmed = command.trim(); return commandTrimmed.startsWith("add"); } private static boolean isCheckCommand(String command) { String commandTrimmed = command.trim(); return commandTrimmed.startsWith("check"); } private static boolean isCompareCommand(String command) { String commandTrimmed = command.trim(); return commandTrimmed.startsWith("compare"); } /** * This function creates an array of VarInfo objects that can represent a set of program * language types provided by the caller. Their names carry no meaning except for the type. * * @param classToTest the invariant class for which the VarInfos must be determined * @param types the types that the VarInfos must have * @return an array of VarInfo objects that have the types corresponding to those in types */ private static VarInfo[] getVarInfos( Class<? extends Invariant> classToTest, ProglangType[] types) { int numInfos = getArity(classToTest); if (numInfos == -1) throw new RuntimeException("Class arity cannot be determined."); VarInfo[] result = new VarInfo[numInfos]; for (int i = 0; i < numInfos; i++) { result[i] = getVarInfo(types[i], i); } return result; } /** * This function returns a VarInfo of the given type. The name is the ith letter of the * alphabet. (Produces variables such that i=0 -> name=a, i=1 -> name=b, ...) * * @param type the desired type that the VarInfo will represent * @param i a unique identifier that determines the name to be used * @return a VarInfo object that described the type */ private static VarInfo getVarInfo(ProglangType type, int i) { assert type != null : "Unexpected null variable type passed to getVarInfo"; String arrayModifier = ""; if (type == ProglangType.INT_ARRAY || type == ProglangType.DOUBLE_ARRAY || type == ProglangType.STRING_ARRAY) { // Is it an array ? arrayModifier = "[]"; } // Create the new VarInfoName dependent on a couple factors: // - If it is an array, attach [] to the name to make parse return // the correct thing // - The base part of the name will be "a" for the first var in an // invariant, "b" for the second, and so on // - The ProglangType will be specified in the parameters // - The comparability will be none VarInfo result = new VarInfo( new String(new char[] {(char) ('a' + i)}) + arrayModifier, type, type, VarComparabilityNone.it, VarInfoAux.getDefault()); return result; } /** * This function determines the arity of a given invariant given its class. * * @param classToTest the invariant type in question * @return the arity of the invariant if it can be determined, -1 otherwise */ private static int getArity(Class<? extends Invariant> classToTest) { if (UnaryInvariant.class.isAssignableFrom(classToTest)) return 1; else if (BinaryInvariant.class.isAssignableFrom(classToTest)) return 2; if (ThreeScalar.class.isAssignableFrom(classToTest)) return 3; return -1; } /** * This function parses a format string -- a space separated list of types -- and determines the * types of objects to be collected. * * @param typeNames the type string for an invariant * @return an array of ProglangTypes representing the data in typeNames */ private static ProglangType[] getTypes(String typeNames) { StringTokenizer stok = new StringTokenizer(typeNames); ProglangType[] result = new ProglangType[stok.countTokens()]; for (int i = 0; i < result.length; i++) { String typeName = stok.nextToken(); // A way of doing the same thing as below in fewer lines of code // Doesn't seem to work... // result[i] = ProglangType.parse(typeName); if (typeName.equalsIgnoreCase("int")) result[i] = ProglangType.INT; else if (typeName.equalsIgnoreCase("double")) result[i] = ProglangType.DOUBLE; else if (typeName.equalsIgnoreCase("string")) result[i] = ProglangType.STRING; else if (typeName.equalsIgnoreCase("int_array")) result[i] = ProglangType.INT_ARRAY; else if (typeName.equalsIgnoreCase("double_array")) result[i] = ProglangType.DOUBLE_ARRAY; else if (typeName.equalsIgnoreCase("string_array")) result[i] = ProglangType.STRING_ARRAY; else return null; assert result[i] != null : "ProglangType unexpectedly parsed to null in getTypes(String)"; } return result; } /** * This function creates an appropriate PptSlice for a given set of VarInfos and a PptTopLevel. * * @param vars an array of VarInfo objects for which the slice is to be created * @param ppt the PptTopLevel object representing the program point * @return a new PptSlice object if the creation of one is possible, else throws a * RuntimeException */ private static PptSlice createSlice(VarInfo[] vars, PptTopLevel ppt) { if (vars.length == 1) { assert vars[0] != null; return new PptSlice1(ppt, vars); } else if (vars.length == 2) { assert vars[0] != null; assert vars[1] != null; return new PptSlice2(ppt, vars); } else if (vars.length == 3) { assert vars[0] != null; assert vars[1] != null; assert vars[2] != null; return new PptSlice3(ppt, vars); } else { throw new RuntimeException( "Improper vars passed to createSlice (length = " + vars.length + ")"); } } /** * This function instantiates an invariant class by using the <type>(PptSlice) constructor. * * @param theClass the invariant class to be instantiated * @param sl the PptSlice representing the variables about which an invariant is determined * @return an instance of the class in theClass if one can be constructed, else throw a * RuntimeException */ private static Invariant instantiateClass(Class<? extends Invariant> theClass, PptSlice sl) { try { Method get_proto = theClass.getMethod("get_proto", new Class<?>[] {}); Invariant proto = (/*@Prototype*/ Invariant) get_proto.invoke(null, new Object[] {}); Invariant inv = proto.instantiate(sl); return (inv); } catch (Exception e) { e.printStackTrace(System.out); throw new RuntimeException( "Error while instantiating invariant " + theClass.getName() + ": " + e.toString()); } } private static String getNextLine(LineNumberReader input) { try { return input.readLine(); } catch (IOException e) { throw new RuntimeException("Exception reading next line"); } } } }
/** * This does the work of main, but it never calls System.exit, so it is appropriate to be called * progrmmatically. Termination of the program with a message to the user is indicated by throwing * Daikon.TerminationMessage. * * @see #main(String[]) * @see daikon.Daikon.TerminationMessage */ public static void mainHelper(final String[] args) throws FileNotFoundException, StreamCorruptedException, OptionalDataException, IOException, ClassNotFoundException { daikon.LogHelper.setupLogs(daikon.LogHelper.INFO); LongOpt[] longopts = new LongOpt[] { new LongOpt(Daikon.config_option_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0), new LongOpt(output_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0), new LongOpt(dir_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0), new LongOpt(conf_SWITCH, LongOpt.NO_ARGUMENT, null, 0), new LongOpt(filter_SWITCH, LongOpt.NO_ARGUMENT, null, 0), new LongOpt(Daikon.debugAll_SWITCH, LongOpt.NO_ARGUMENT, null, 0), new LongOpt(Daikon.debug_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0), new LongOpt(Daikon.ppt_regexp_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0), new LongOpt(Daikon.track_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0), }; Getopt g = new Getopt("daikon.tools.InvariantChecker", args, "h", longopts); int c; while ((c = g.getopt()) != -1) { switch (c) { case 0: // got a long option String option_name = longopts[g.getLongind()].getName(); if (Daikon.help_SWITCH.equals(option_name)) { System.out.println(usage); throw new Daikon.TerminationMessage(); } else if (conf_SWITCH.equals(option_name)) { doConf = true; } else if (filter_SWITCH.equals(option_name)) { doFilter = true; } else if (dir_SWITCH.equals(option_name)) { dir_file = new File(g.getOptarg()); if (!dir_file.exists() || !dir_file.isDirectory()) throw new Daikon.TerminationMessage("Error reading the directory " + dir_file); } else if (output_SWITCH.equals(option_name)) { output_file = new File(g.getOptarg()); output_stream = new PrintStream(new FileOutputStream(output_file)); } else if (Daikon.config_option_SWITCH.equals(option_name)) { String item = g.getOptarg(); daikon.config.Configuration.getInstance().apply(item); break; } else if (Daikon.debugAll_SWITCH.equals(option_name)) { Global.debugAll = true; } else if (Daikon.debug_SWITCH.equals(option_name)) { LogHelper.setLevel(g.getOptarg(), LogHelper.FINE); } else if (Daikon.track_SWITCH.equals(option_name)) { LogHelper.setLevel("daikon.Debug", LogHelper.FINE); String error = Debug.add_track(g.getOptarg()); if (error != null) { throw new Daikon.TerminationMessage( "Error parsing track argument '" + g.getOptarg() + "' - " + error); } } else { throw new RuntimeException("Unknown long option received: " + option_name); } break; case 'h': System.out.println(usage); throw new Daikon.TerminationMessage(); case '?': break; // getopt() already printed an error default: System.out.println("getopt() returned " + c); break; } } // Loop through each filename specified for (int i = g.getOptind(); i < args.length; i++) { // Get the file and make sure it exists File file = new File(args[i]); if (!file.exists()) { throw new Error("File " + file + " not found."); } // These aren't "endsWith()" because there might be a suffix on the end // (eg, a date). String filename = file.toString(); if (filename.indexOf(".inv") != -1) { if (inv_file != null) { throw new Daikon.TerminationMessage("multiple inv files specified", usage); } inv_file = file; } else if (filename.indexOf(".dtrace") != -1) { dtrace_files.add(filename); } else { throw new Error("Unrecognized argument: " + file); } } if (dir_file == null) { checkInvariants(); return; } // Yoav additions: File[] filesInDir = dir_file.listFiles(); if (filesInDir == null || filesInDir.length == 0) throw new Daikon.TerminationMessage("The directory " + dir_file + " is empty", usage); ArrayList<File> invariants = new ArrayList<File>(); for (File f : filesInDir) if (f.toString().indexOf(".inv") != -1) invariants.add(f); if (invariants.size() == 0) throw new Daikon.TerminationMessage( "Did not find any invariant files in the directory " + dir_file, usage); ArrayList<File> dtraces = new ArrayList<File>(); for (File f : filesInDir) if (f.toString().indexOf(".dtrace") != -1) dtraces.add(f); if (dtraces.size() == 0) throw new Daikon.TerminationMessage( "Did not find any dtrace files in the directory " + dir_file, usage); System.out.println( "Collecting data for invariants files " + invariants + " and dtrace files " + dtraces); dtrace_files.clear(); for (File dtrace : dtraces) { dtrace_files.add(dtrace.toString()); } String commaLine = ""; for (File inFile : invariants) { String name = inFile.getName().replace(".inv", "").replace(".gz", ""); commaLine += "," + name; } outputComma.add(commaLine); commaLine = ""; for (File inFile : invariants) { inv_file = inFile; failedInvariants.clear(); testedInvariants.clear(); error_cnt = 0; output_stream = new PrintStream( new FileOutputStream( inFile.toString().replace(".inv", "").replace(".gz", "") + ".false-positives.txt")); checkInvariants(); output_stream.close(); int failedCount = failedInvariants.size(); int testedCount = testedInvariants.size(); String percent = toPercentage(failedCount, testedCount); commaLine += "," + percent; } outputComma.add(commaLine); System.out.println(); for (String output : outputComma) System.out.println(output); }