/** * DaikonSimple reads a declaration file and trace file and outputs a list of likely invariants * using the simple incremental algorithm. Its methods parallel those of Daikon but oftentimes * certain checks are eliminated from DaikonSimple's methods because there is less filtering of * invariants and variables. * * <p>DaikonSimple was written to check the implementation of the optimizations in Daikon. * DaikonSimple does not use an optimizations, and its processing will produce a complete set of * true invariants. Daikon does have flags to "turn off" some of its optimizations but there are * some optimizations are built into the way Daikon processes the samples (e.g. variable hierarchy * and bottom up processing). In addition, we want to check the optimizations, so we don't want to * bypass them. In Daikon, code was written to "undo" the optimizations, so we could recover the * invariants that were previously filtered out or not created (see Daikon.dkconfig_undo_opts flag). * By comparing the output from the two, we can find problems with the optimization implementation * by tracking the cause of the differences. */ @SuppressWarnings("nullness") public class DaikonSimple { // logging information public static final Logger debug = Logger.getLogger("daikon.DaikonSimple"); public static final Logger debug_detail = Logger.getLogger("daikon.DaikonSimple.Detail"); // // inv file for storing the invariants in serialized form // public static File inv_file = null; private static String usage = UtilMDE.join( new String[] { "", "Usage: java daikon.DaikonSimple [OPTION]... <decls_file> <dtrace_file>", " -h, --" + Daikon.help_SWITCH, " Display this usage message", // " -o, <inv_file> ", // " Writes output to <inv_file>", " --" + Daikon.debugAll_SWITCH, " Turns on all debug flags (voluminous output)", " --" + Daikon.debug_SWITCH + " logger", " Turns on the specified debug logger", " --" + Daikon.track_SWITCH + " class<var1,var2,var3>@ppt", " Print debug info on the specified invariant class, vars, and ppt", }, lineSep); // a pptMap that contains all the program points public static PptMap all_ppts; public static void main(final String[] args) throws IOException, FileNotFoundException { try { mainHelper(args); } catch (Daikon.TerminationMessage e) { String message = e.getMessage(); if (Daikon.dkconfig_show_stack_trace) e.printStackTrace(); if (message != null) { System.err.println(message); System.exit(1); } System.exit(0); } // Any exception other than Daikon.TerminationMessage gets propagated. // This simplifies debugging by showing the stack trace. } /** * 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. * * <p>Difference from Daikon's mainHelper: turn off optimization flags (equality, dynamic * constants, NIS suppression). * * @see #main(String[]) * @see daikon.Daikon.TerminationMessage * @see daikon.Daikon#mainHelper(String[]) */ public static void mainHelper(final String[] args) throws IOException, FileNotFoundException { // set up logging information daikon.LogHelper.setupLogs(daikon.LogHelper.INFO); // No optimizations used in the simple incremental algorithm so // optimizations are turned off. Daikon.use_equality_optimization = false; Daikon.dkconfig_use_dynamic_constant_optimization = false; Daikon.suppress_implied_controlled_invariants = false; NIS.dkconfig_enabled = false; // The flag tells FileIO and Daikon to use DaikonSimple // specific methods (e.g. FileIO.read_declaration_file). // When FileIO reads and processes // samples, it must use the SimpleProcessor rather than the // default Processor. Daikon.using_DaikonSimple = true; // Read command line options Daikon.FileOptions files = Daikon.read_options(args, usage); // DaikonSimple does not supply nor use the spinfo_files and map_files Set<File> decls_files = files.decls; Set<String> dtrace_files = files.dtrace; if ((decls_files.size() == 0) && (dtrace_files.size() == 0)) { throw new Daikon.TerminationMessage("No .decls or .dtrace files specified"); } // Create the list of all invariant types Daikon.setup_proto_invs(); // Create the program points for enter and numbered exits and // initializes the points (adding orig and derived variables) all_ppts = FileIO.read_declaration_files(decls_files); // Create the combined exits (and add orig and derived vars) // Daikon.create_combined_exits(all_ppts); // Read and process the data trace files SimpleProcessor processor = new SimpleProcessor(); FileIO.read_data_trace_files(dtrace_files, all_ppts, processor, true); // System.exit(0); // Print out the invariants for each program point (sort first) for (Iterator<PptTopLevel> t = all_ppts.pptIterator(); t.hasNext(); ) { PptTopLevel ppt = t.next(); // We do not need to print out program points that have not seen // any samples. if (ppt.num_samples() == 0) { continue; } List<Invariant> invs = PrintInvariants.sort_invariant_list(ppt.invariants_vector()); List<Invariant> filtered_invs = Daikon.filter_invs(invs); // The dkconfig_quiet printing is used for creating diffs between // DaikonSimple // and Daikon's output. The second kind of printing is used for // debugging. Since the names of the program points are the same for both // Daikon and DaikonSimple, diffing the two output will result in // only differences in the invariants, but we can not see at which program // points these differing invariants appear. Using the second kind of // printing, // Daikon's output does not have the '+' in the program point name, so in // addition // to the invariants showing up in the diff, we will also see the program // point // names. if (Daikon.dkconfig_quiet) { System.out.println("===================================================="); System.out.println(ppt.name()); } else { System.out.println("===================================================+"); System.out.println(ppt.name() + " +"); } // Sometimes the program points actually differ in number of // samples seen due to differences in how Daikon and DaikonSimple // see the variable hierarchy. System.out.println(ppt.num_samples()); for (Invariant inv : filtered_invs) { System.out.println(inv.getClass()); System.out.println(inv); } } } /** * Install views and the invariants. Duplicated from PptTopLevel's version because DaikonSimple * needs to use its own version of slice checking code. * * <p>Difference from PptTopLevel's version: 1. canonical (leader of equality set) check of * variables is turned off because every variable is in its own equality set 2. debugging * information turned off because DaikonSimple's code is more contained 3. less constraints on the * slices * * @see daikon.PptTopLevel#instantiate_views_and_invariants() */ // Note that some slightly inefficient code has been added to aid // in debugging. When creating binary and ternary views and debugging // is on, the outer loops will not terminate prematurely on innapropriate // (i.e., non-canonical) variables. This allows explicit debug statements // for each possible combination, simplifying determining why certain // slices were not created. // // Note that '///*' indicates code duplicated from PptTopLevel's // version but commented out because DaikonSimple does not need // to perform these checks public static void instantiate_views_and_invariants(PptTopLevel ppt) { // used only for debugging int old_num_vars = ppt.var_infos.length; int old_num_views = ppt.numViews(); boolean debug_on = debug.isLoggable(Level.FINE); // / 1. all unary views // Unary slices/invariants. // Currently, there are no constraints on the unary // slices. Since we are trying to create all of the invariants, the // variables does not have to be a leader and can be a constant. // Note that the always missing check is only applicable when the // dynamic constants optimization is turned on (so we do not do the // check here). Vector<PptSlice> unary_views = new Vector<PptSlice>(ppt.var_infos.length); for (VarInfo vi : ppt.var_infos) { // /* if (!is_slice_ok(vi)) // /* continue; PptSlice1 slice1 = new PptSlice1(ppt, vi); slice1.instantiate_invariants(); unary_views.add(slice1); } ppt.addViews(unary_views); unary_views = null; // / 2. all binary views // Binary slices/invariants. Vector<PptSlice> binary_views = new Vector<PptSlice>(); for (int i1 = 0; i1 < ppt.var_infos.length; i1++) { VarInfo var1 = ppt.var_infos[i1]; // Variables can be constant and missing in DaikonSimple invariants // /* if (!is_var_ok_binary(var1)) // /* continue; for (int i2 = i1; i2 < ppt.var_infos.length; i2++) { VarInfo var2 = ppt.var_infos[i2]; // Variables can be constant and missing in DaikonSimple invariants // /* if (!is_var_ok_binary(var2)) // /* continue; if (!(var1.compatible(var2) || (var1.type.isArray() && var1.eltsCompatible(var2)) || (var2.type.isArray() && var2.eltsCompatible(var1)))) { continue; } PptSlice2 slice2 = new PptSlice2(ppt, var1, var2); slice2.instantiate_invariants(); binary_views.add(slice2); } } ppt.addViews(binary_views); binary_views = null; // 3. all ternary views Vector<PptSlice> ternary_views = new Vector<PptSlice>(); for (int i1 = 0; i1 < ppt.var_infos.length; i1++) { VarInfo var1 = ppt.var_infos[i1]; if (!is_var_ok(var1)) continue; for (int i2 = i1; i2 < ppt.var_infos.length; i2++) { VarInfo var2 = ppt.var_infos[i2]; if (!is_var_ok(var2)) continue; for (int i3 = i2; i3 < ppt.var_infos.length; i3++) { VarInfo var3 = ppt.var_infos[i3]; if (!is_var_ok(var3)) continue; if (!is_slice_ok(var1, var2, var3)) { continue; } PptSlice3 slice3 = new PptSlice3(ppt, var1, var2, var3); slice3.instantiate_invariants(); ternary_views.add(slice3); } } } ppt.addViews(ternary_views); // This method didn't add any new variables. assert old_num_vars == ppt.var_infos.length; ppt.repCheck(); } // This method is exclusively for checking variables participating // in ternary invariants. The variable must be integer or float, and // can not be an array. public static boolean is_var_ok(VarInfo var) { return (var.file_rep_type.isIntegral() || var.file_rep_type.isFloat()) && !var.rep_type.isArray(); } /** * Returns whether or not the specified binary slice should be created. The slice should not be * created if the vars not compatible. * * <p>Since we are trying to create all of the invariants, the variables does not have to be a * leader and can be a constant. Note that the always missing check is only applicable when the * dynamic constants optimization is turned on (so we do not do the check here). * * @see daikon.PptTopLevel#is_slice_ok(VarInfo, VarInfo) */ public static boolean is_slice_ok(VarInfo v1, VarInfo v2) { return v1.compatible(v2); } /** * Returns whether or not the specified ternary slice should be created. The slice should not be * created if any of the following are true - Any var is an array - Any of the vars are not * compatible with the others - Any var is not (integral or float) * * <p>Since we are trying to create all of the invariants, the variables does not have to be a * leader and can be a constant. Note that the always missing check is only applicable when the * dynamic constants optimization is turned on (so we do not do the check here). In addition, we * do want to create the reflexive ones and partially reflexive invariants. * * @see daikon.PptTopLevel#is_slice_ok(VarInfo, VarInfo, VarInfo) */ public static boolean is_slice_ok(VarInfo v1, VarInfo v2, VarInfo v3) { // Vars must be compatible return (v1.compatible(v2) && v1.compatible(v3) && v2.compatible(v3)); } /** * The Call class helps the SimpleProcessor keep track of matching enter and exit program points * and also object program points. Each Call object represents one entry in the dtrace file, i.e. * enter, exit, object entry. */ static final class Call { public PptTopLevel ppt; public ValueTuple vt; public Call(PptTopLevel ppt, ValueTuple vt) { this.ppt = ppt; this.vt = vt; } } /** The SimpleProcessor class processes each sample in the dtrace file. */ public static class SimpleProcessor extends FileIO.Processor { PptMap all_ppts = null; /** nonce -> List<Call,Call> * */ // The first Call is the enter entry and the second is the object entry Map<Integer, List<Call>> call_map = new LinkedHashMap<Integer, List<Call>>(); // Flag for whether there are out of order entries in the // dtrace file. For unterminated calls (enter but // not exit entry in the dtrace file), because DaikonSimple had // processed each entry separately (not bottom up like Daikon), // DaikonSimple applied the enter and object call before seeing the // exit call, which is not consistent with Daikon. Daikon does not // process unterminated method calls. // The method of holding the enter and object calls until finding // a matching exit call assumes: // - enter always comes before exit // - first entry in dtrace is an enter // - order in dtrace is enter, exit, object [for constructors] or // enter, object, exit, object [for methods] but not necessarily // sequential boolean wait = false; // pointer to last nonce so we can associate the object entry // with the right enter entry Integer last_nonce = new Integer(-1); /** * Creates a valuetuple for the receiver using the vt of the original. The method copies over * the values of variables shared by both program points and sets the rest of the variables in * the receiver's valuetuple as missing. Also, adds the orig and derived variables to the * receiver and returns the newly created valuetuple. */ private static ValueTuple copySample( PptTopLevel receiver, PptTopLevel original, ValueTuple vt, int nonce) { // Make the vt for the receiver ppt // Object values[] = new Object[receiver.num_tracevars]; // int mods[] = new int[receiver.num_tracevars]; Object values[] = new Object[receiver.var_infos.length - receiver.num_static_constant_vars]; int mods[] = new int[receiver.var_infos.length - receiver.num_static_constant_vars]; // Build the vt for the receiver ppt by looking through the current // vt and filling in the gaps. int k = 0; for (Iterator<VarInfo> i = receiver.var_info_iterator(); i.hasNext(); ) { VarInfo var = i.next(); if (var.is_static_constant) continue; boolean found = false; for (Iterator<VarInfo> j = original.var_info_iterator(); j.hasNext(); ) { VarInfo var2 = j.next(); if (var.name().equals(var2.name())) { values[k] = vt.getValueOrNull(var2); mods[k] = vt.getModified(var2); found = true; break; } } if (!found) { values[k] = null; mods[k] = 2; } k++; } ValueTuple receiver_vt = new ValueTuple(values, mods); FileIO.add_orig_variables(receiver, receiver_vt.vals, receiver_vt.mods, nonce); FileIO.add_derived_variables(receiver, receiver_vt.vals, receiver_vt.mods); return receiver_vt; } /** * Process the sample by checking it against each existing invariant at the program point and * removing the invariant from the list of possibles if any invariant is falsified. */ public void process_sample( PptMap all_ppts, PptTopLevel ppt, ValueTuple vt, /*@Nullable*/ Integer nonce) { this.all_ppts = all_ppts; // Add samples to orig and derived variables FileIO.add_orig_variables(ppt, vt.vals, vt.mods, nonce); FileIO.add_derived_variables(ppt, vt.vals, vt.mods); // Intern the sample vt = new ValueTuple(vt.vals, vt.mods); // DaikonSimple must make the object program point manually because // the new Chicory produced dtrace files do not contain object ppts // in the dtrace part of the file (the program point is declared). // Make the object ppt PptName ppt_name = ppt.ppt_name; PptTopLevel object_ppt = null; PptTopLevel class_ppt = null; ValueTuple object_vt = null; ValueTuple class_vt = null; if ((ppt_name.isEnterPoint() && !ppt_name.isConstructor()) || ppt_name.isExitPoint()) { object_ppt = all_ppts.get(ppt_name.makeObject()); class_ppt = all_ppts.get(ppt_name.makeClassStatic()); } // C programs do not have object ppts // check whether the ppt is a static or instance method // that decides whether the sample is copied over to the object and/or // class ppt if (object_ppt != null) { // the check assumes that static fields are not stored first in the // object ppt if (ppt.find_var_by_name(object_ppt.var_infos[0].name()) != null) { // object and class ppt should be created object_vt = copySample(object_ppt, ppt, vt, nonce); if (class_ppt != null) { class_vt = copySample(class_ppt, ppt, vt, nonce); } } else { // only class ppt should be created if (class_ppt != null) { class_vt = copySample(class_ppt, ppt, vt, nonce); } object_vt = null; object_ppt = null; } } // If this is an enter point, just remember it for later if (ppt_name.isEnterPoint()) { assert nonce != null; assert call_map.get(nonce) == null; List<Call> value = new ArrayList<Call>(); value.add(new Call(ppt, vt)); if (object_ppt != null) { value.add(new Call(object_ppt, object_vt)); } if (class_ppt != null) { value.add(new Call(class_ppt, class_vt)); } call_map.put(nonce, value); last_nonce = nonce; wait = true; return; } // If this is an exit point, process the saved enter (and sometimes // object) point if (ppt_name.isExitPoint()) { assert nonce != null; List<Call> value = call_map.remove(nonce); add(ppt, vt, nonce); for (Call ec : value) { add(ec.ppt, ec.vt, nonce); } wait = false; } if (object_ppt != null) add(object_ppt, object_vt, nonce); // apply object vt if (class_ppt != null) add(class_ppt, class_vt, nonce); } // The method iterates through all of the invariants in the ppt // and manually adds the sample to the invariant and removing the // invariant if it is falsified private void add(PptTopLevel ppt, ValueTuple vt, int nonce) { // if this is a numbered exit, apply to the combined exit as well if (ppt.ppt_name.isNumberedExitPoint()) { // Daikon.create_combined_exits(all_ppts); PptTopLevel parent = all_ppts.get(ppt.ppt_name.makeExit()); if (parent != null) { parent.get_missingOutOfBounds(ppt, vt); add(parent, vt, nonce); } else { // make parent and apply // this is a hack. it should probably filter out orig and derived // vars instead of taking the first n. int len = ppt.num_tracevars + ppt.num_static_constant_vars; VarInfo[] exit_vars = new VarInfo[len]; for (int j = 0; j < len; j++) { exit_vars[j] = new VarInfo(ppt.var_infos[j]); exit_vars[j].varinfo_index = ppt.var_infos[j].varinfo_index; exit_vars[j].value_index = ppt.var_infos[j].value_index; exit_vars[j].equalitySet = null; } parent = new PptTopLevel(ppt.ppt_name.makeExit().getName(), exit_vars); Daikon.init_ppt(parent, all_ppts); all_ppts.add(parent); parent.get_missingOutOfBounds(ppt, vt); add(parent, vt, nonce); } } // If the point has no variables, skip it if (ppt.var_infos.length == 0) { // The sample should be skipped but Daikon does not do this so // DaikonSimple will not do this to be consistent. // The better idea is for Daikon to assert that these valuetuples are // empty and then skip the sample. assert vt.size() == 0; return; } // Instantiate slices and invariants if this is the first sample if (ppt.num_samples() == 0) { instantiate_views_and_invariants(ppt); } // manually inc the sample number because DaikonSimple does not // use any of PptTopLevel's add methods which increase the sample // number ppt.incSampleNumber(); // Loop through each slice for (Iterator<PptSlice> i = ppt.views_iterator(); i.hasNext(); ) { PptSlice slice = i.next(); Iterator<Invariant> k = slice.invs.iterator(); boolean missing = false; for (VarInfo v : slice.var_infos) { // If any var has encountered out of array bounds values, // stop all invariants in this slice. The presumption here is that // an index out of bounds implies that the derived variable (eg a[i]) // doesn't really make any sense (essentially that i is not a valid // index for a). Invariants on the derived variable are thus not // relevant. // If any variables are out of bounds, remove the invariants if (v.missingOutOfBounds()) { while (k.hasNext()) { Invariant inv = k.next(); k.remove(); } missing = true; break; } // If any variables are missing, skip this slice if (v.isMissing(vt)) { missing = true; break; } } // keep a list of the falsified invariants if (!missing) { while (k.hasNext()) { Invariant inv = k.next(); Invariant pre_inv = inv.clone(); for (VarInfo vi : inv.ppt.var_infos) { assert vt.getValue(vi) != null : vi; } if (inv.ppt instanceof PptSlice2) assert inv.ppt.var_infos.length == 2; InvariantStatus status = inv.add_sample(vt, 1); if (status == InvariantStatus.FALSIFIED) { k.remove(); } } } // update num_samples and num_values of a slice manually // because DaikonSimple does not call any of PptTopLevel's // add methods for (int j = 0; j < vt.vals.length; j++) { if (!vt.isMissing(j)) { ValueSet vs = ppt.value_sets[j]; vs.add(vt.vals[j]); } } ppt.mbtracker.add(vt, 1); } } } }
/** * InvariantChecker reads an invariant file and trace file. It prints errors for any invariants that * are violated by the trace file. */ public class InvariantChecker { private InvariantChecker() { throw new Error("do not instantiate"); } public static final Logger debug = Logger.getLogger("daikon.tools.InvariantChecker"); public static final Logger debug_detail = Logger.getLogger("daikon.tools.InvariantCheckerDetail"); private static final String output_SWITCH = "output"; private static final String dir_SWITCH = "dir"; private static final String conf_SWITCH = "conf"; private static final String filter_SWITCH = "filter"; private static String usage = UtilMDE.joinLines( "Usage: java daikon.InvariantChecker [OPTION]... <inv_file> " + "<dtrace_file>", " -h, --" + Daikon.help_SWITCH, " Display this usage message", " --" + output_SWITCH + " output file", " --" + conf_SWITCH, " Checks only invariants that are above the default confidence level", " --" + filter_SWITCH, " Checks only invariants that are not filtered by the default filters", " --" + dir_SWITCH + " directory with invariant and dtrace files", " We output how many invariants failed for each invariant file. We check for failure against any sample in any dtrace file.", " --" + Daikon.config_option_SWITCH + " config_var=val", " Sets the specified configuration variable. ", " --" + Daikon.debugAll_SWITCH, " Turns on all debug flags (voluminous output)", " --" + Daikon.debug_SWITCH + " logger", " Turns on the specified debug logger", " --" + Daikon.track_SWITCH + " class<var1,var2,var3>@ppt", " Print debug info on the specified invariant class, vars, and ppt"); public static File inv_file = null; public static List<String> dtrace_files = new ArrayList<String>(); static File output_file; static PrintStream output_stream = System.out; static int error_cnt = 0; static int sample_cnt = 0; static File dir_file; // Yoav added static boolean doFilter; static boolean doConf; static boolean quiet = true; static HashSet<Invariant> failedInvariants = new HashSet<Invariant>(); // Yoav added static HashSet<Invariant> testedInvariants = new HashSet<Invariant>(); // Yoav added static HashSet<Invariant> activeInvariants = new HashSet<Invariant>(); // Yoav added static LinkedHashSet<String> outputComma = new LinkedHashSet<String>(); // Yoav added public static void main(String[] args) throws FileNotFoundException, StreamCorruptedException, OptionalDataException, IOException, ClassNotFoundException { try { if (args.length == 0) { throw new Daikon.TerminationMessage(usage); } mainHelper(args); } catch (Daikon.TerminationMessage e) { System.err.println(e.getMessage()); System.exit(1); } // Any exception other than Daikon.TerminationMessage gets propagated. // This simplifies debugging by showing the stack trace. } /** * 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); } private static String toPercentage(int portion, int total) { double s = portion * 100; return String.format("%.2f", s / total) + "%"; } private static void checkInvariants() throws IOException { // Read the invariant file PptMap ppts = FileIO.read_serialized_pptmap(inv_file, true); // Yoav: make sure we have unique invariants InvariantFilters fi = InvariantFilters.defaultFilters(); // Set<String> allInvariantsStr = new HashSet<String>(); Set<Invariant> allInvariants = new HashSet<Invariant>(); for (PptTopLevel ppt : ppts.all_ppts()) for (Iterator<PptSlice> i = ppt.views_iterator(); i.hasNext(); ) { PptSlice slice = i.next(); for (Invariant inv : slice.invs) { if (doConf && inv.getConfidence() < Invariant.dkconfig_confidence_limit) { // System.out.printf ("inv ignored (conf): %s:%s\n", inv.ppt.name(), // inv.format()); continue; } if (doFilter && fi.shouldKeep(inv) == null) { // System.out.printf ("inv ignored (filter): %s:%s\n", // inv.ppt.name(), inv.format()); continue; } activeInvariants.add(inv); // String n = invariant2str(ppt, inv); // if (!allInvariants.contains(inv) && allInvariantsStr.contains(n)) throw new // Daikon.TerminationMessage("Two invariants have the same ppt.name+inv.rep:"+n); allInvariants.add(inv); // allInvariantsStr.add(n); } } // Read and process the data trace files FileIO.Processor processor = new InvariantCheckProcessor(); Daikon.FileIOProgress progress = new Daikon.FileIOProgress(); progress.start(); progress.clear(); FileIO.read_data_trace_files(dtrace_files, ppts, processor, false); progress.shouldStop = true; System.out.println(); System.out.printf( "%s: %,d errors found in %,d samples (%s)\n", inv_file, error_cnt, sample_cnt, toPercentage(error_cnt, sample_cnt)); int failedCount = failedInvariants.size(); int testedCount = testedInvariants.size(); String percent = toPercentage(failedCount, testedCount); System.out.println( inv_file + ": " + failedCount + " false positives, out of " + testedCount + ", which is " + percent + "."); if (false) { for (Invariant inv : failedInvariants) { System.out.printf("+%s:%s\n", inv.ppt.name(), inv.format()); } } } /** Class to track matching ppt and its values. */ static final class EnterCall { public PptTopLevel ppt; public ValueTuple vt; public EnterCall(PptTopLevel ppt, ValueTuple vt) { this.ppt = ppt; this.vt = vt; } } public static class InvariantCheckProcessor extends FileIO.Processor { PptMap all_ppts = null; Map<Integer, EnterCall> call_map = new LinkedHashMap<Integer, EnterCall>(); /** * process the sample by checking it against each existing invariant and issuing an error if any * invariant is falsified or weakened. */ public void process_sample(PptMap all_ppts, PptTopLevel ppt, ValueTuple vt, Integer nonce) { this.all_ppts = all_ppts; debug.fine("processing sample from: " + ppt.name); // Add orig and derived variables FileIO.add_orig_variables(ppt, vt.vals, vt.mods, nonce); FileIO.add_derived_variables(ppt, vt.vals, vt.mods); // Intern the sample vt = new ValueTuple(vt.vals, vt.mods); // If this is an enter point, just remember it for later if (ppt.ppt_name.isEnterPoint()) { Assert.assertTrue(nonce != null); if (dir_file != null) { // Yoav: I had to do a hack to handle the case that several dtrace files are concatenated // together, // and Sung's dtrace files have unterminated calls, and when concatenating two files you // can have the same nonce. // So I have to remove the nonce found from the call_map call_map.remove(nonce); } else Assert.assertTrue(call_map.get(nonce) == null); call_map.put(nonce, new EnterCall(ppt, vt)); debug.fine("Skipping enter sample"); return; } // If this is an exit point, process the saved enter point if (ppt.ppt_name.isExitPoint()) { Assert.assertTrue(nonce != null); EnterCall ec = call_map.get(nonce); if (ec != null) { call_map.remove(nonce); debug.fine("Processing enter sample from " + ec.ppt.name); add(ec.ppt, ec.vt); } else { // didn't find the enter if (!quiet) System.out.printf("couldn't find enter for nonce %d at ppt %s\n", nonce, ppt.name()); return; } } add(ppt, vt); } private void add(PptTopLevel ppt, ValueTuple vt) { // Add the sample to any splitters if (ppt.has_splitters()) { for (PptSplitter ppt_split : ppt.splitters) { PptConditional ppt_cond = ppt_split.choose_conditional(vt); if (ppt_cond != null) add(ppt_cond, vt); else debug.fine(": sample doesn't pick conditional"); } } // if this is a numbered exit, apply to the combined exit as well if (!(ppt instanceof PptConditional) && ppt.ppt_name.isNumberedExitPoint()) { PptTopLevel parent = all_ppts.get(ppt.ppt_name.makeExit()); if (parent != null) { parent.get_missingOutOfBounds(ppt, vt); add(parent, vt); } } // If the point has no variables, skip it if (ppt.var_infos.length == 0) return; // We should have received sample here before, or there is nothing // to check. // Yoav added: It can be that the different dtrace and inv files have different program points if (false && ppt.num_samples() <= 0) Assert.assertTrue( ppt.num_samples() > 0, "ppt " + ppt.name + " has 0 samples and " + ppt.var_infos.length + " variables"); // Loop through each slice slice_loop: for (Iterator<PptSlice> i = ppt.views_iterator(); i.hasNext(); ) { PptSlice slice = i.next(); if (debug_detail.isLoggable(Level.FINE)) debug_detail.fine( ": processing slice " + slice + "vars: " + Debug.toString(slice.var_infos, vt)); // If any variables are missing, skip this slice for (int j = 0; j < slice.var_infos.length; j++) { VarInfo v = slice.var_infos[j]; int mod = vt.getModified(v); if (v.isMissing(vt)) { if (debug_detail.isLoggable(Level.FINE)) debug_detail.fine(": : Skipping slice, " + v.name() + " missing"); continue slice_loop; } if (v.missingOutOfBounds()) { if (debug_detail.isLoggable(Level.FINE)) debug.fine(": : Skipping slice, " + v.name() + " out of bounds"); continue slice_loop; } } // Loop through each invariant for (Invariant inv : slice.invs) { if (debug_detail.isLoggable(Level.FINE)) debug_detail.fine(": : Processing invariant: " + inv); if (!inv.isActive()) { if (debug_detail.isLoggable(Level.FINE)) debug_detail.fine(": : skipped non-active " + inv); continue; } // Yoav added if (!activeInvariants.contains(inv)) { // System.out.printf ("skipping invariant %s:%s\n", inv.ppt.name(), // inv.format()); continue; } // String invRep = invariant2str(ppt, inv); testedInvariants.add(inv); InvariantStatus status = inv.add_sample(vt, 1); sample_cnt++; if (status != InvariantStatus.NO_CHANGE) { LineNumberReader lnr = FileIO.data_trace_state.reader; String line = (lnr == null) ? "?" : String.valueOf(lnr.getLineNumber()); if (!quiet) { output_stream.println( "At ppt " + ppt.name + ", Invariant '" + inv.format() + "' invalidated by sample " + Debug.toString(slice.var_infos, vt) + "at line " + line + " in file " + FileIO.data_trace_state.filename); } failedInvariants.add(inv); activeInvariants.remove(inv); error_cnt++; } } } } } private static String invariant2str(PptTopLevel ppt, Invariant inv) { return ppt.name + " == " + inv.repr() + inv.getClass() + inv.varNames() + ": " + inv.format(); } }