001package daikon; 002 003import gnu.getopt.Getopt; 004import gnu.getopt.LongOpt; 005import java.io.File; 006import org.plumelib.util.FilesPlume; 007import org.plumelib.util.StringsPlume; 008 009/** 010 * UnionInvariants is a command-line tool that will read in one (or more) {@code .inv} files 011 * (possibly gzipped) and write their union into a new {@code .inv} file (possibly gzipped). Run 012 * with {@code -h} flag to view the command line syntax. 013 * 014 * <p>Currently, UnionInvariants works at program point granularity, so two inv files cannot have 015 * printable invariants at the same program point. 016 * 017 * <p>You can optionally use Simplify after combination in case you believe invariant context from 018 * other types will suppress some invariants. (This tool is also a nice way to run Simplify on a 019 * single inv file.) 020 */ 021public final class UnionInvariants { 022 private UnionInvariants() { 023 throw new Error("do not instantiate"); 024 } 025 026 /** The usage message for this program. */ 027 // Non-empty program points in the input files must be distinct. 028 private static String usage = 029 StringsPlume.joinLines( 030 "Usage: java daikon.UnionInvariants [OPTION]... FILE.inv[.gz] [FILE.inv[.gz] ...]", 031 " -h, --" + Daikon.help_SWITCH, 032 " Display this usage message", 033 " --" + Daikon.suppress_redundant_SWITCH, 034 " Suppress display of logically redundant invariants."); 035 036 public static void main(final String[] args) throws Exception { 037 try { 038 mainHelper(args); 039 } catch (Daikon.DaikonTerminationException e) { 040 Daikon.handleDaikonTerminationException(e); 041 } 042 } 043 044 /** 045 * This does the work of {@link #main(String[])}, but it never calls System.exit, so it is 046 * appropriate to be called progrmmatically. 047 */ 048 public static void mainHelper(String[] args) throws Exception { 049 File inv_file = null; 050 051 LongOpt[] longopts = 052 new LongOpt[] { 053 new LongOpt(Daikon.suppress_redundant_SWITCH, LongOpt.NO_ARGUMENT, null, 0), 054 }; 055 Getopt g = new Getopt("daikon.UnionInvariants", args, "ho:", longopts); 056 int c; 057 while ((c = g.getopt()) != -1) { 058 switch (c) { 059 case 0: 060 // got a long option 061 String option_name = longopts[g.getLongind()].getName(); 062 if (Daikon.help_SWITCH.equals(option_name)) { 063 System.out.println(usage); 064 throw new Daikon.NormalTermination(); 065 } else if (Daikon.suppress_redundant_SWITCH.equals(option_name)) { 066 Daikon.suppress_redundant_invariants_with_simplify = true; 067 } else { 068 throw new Daikon.UserError("Unknown option received: " + option_name); 069 } 070 break; 071 case 'h': 072 System.out.println(usage); 073 throw new Daikon.NormalTermination(); 074 case 'o': 075 String inv_filename = Daikon.getOptarg(g); 076 077 if (inv_file != null) { 078 throw new Daikon.UserError( 079 "multiple serialization output files supplied on command line: " 080 + inv_file 081 + " " 082 + inv_filename); 083 } 084 085 System.out.println("Inv filename = " + inv_filename); 086 inv_file = new File(inv_filename); 087 088 if (!FilesPlume.canCreateAndWrite(inv_file)) { 089 throw new Daikon.UserError("Cannot write to serialization output file " + inv_file); 090 } 091 break; 092 // 093 case '?': 094 break; // getopt() already printed an error 095 default: 096 System.out.println("getopt() returned " + c); 097 break; 098 } 099 } 100 101 // The index of the first non-option argument 102 int fileIndex = g.getOptind(); 103 if ((inv_file == null) || (args.length - fileIndex == 0)) { 104 System.out.println(usage); 105 throw new Daikon.UserError("Wrong number of args"); 106 } 107 108 PptMap result = new PptMap(); 109 for (int i = fileIndex; i < args.length; i++) { 110 String filename = args[i]; 111 System.out.println("Reading " + filename + "..."); 112 PptMap ppt_map = 113 FileIO.read_serialized_pptmap( 114 new File(filename), true // use saved config 115 ); 116 union(result, ppt_map); 117 } 118 119 // TODO: We should check consistency things, such as entry_ppt not 120 // pointing outside of the PptMap. (What else?) 121 122 // Mark redundant invariants (may have more given additional 123 // surrounding program points) 124 125 if (Daikon.suppress_redundant_invariants_with_simplify) { 126 System.out.print("Invoking Simplify to identify redundant invariants..."); 127 System.out.flush(); 128 long start = System.currentTimeMillis(); 129 for (PptTopLevel ppt : result.pptIterable()) { 130 ppt.mark_implied_via_simplify(result); 131 } 132 long end = System.currentTimeMillis(); 133 double elapsed = (end - start) / 1000.0; 134 System.out.println(new java.text.DecimalFormat("#.#").format(elapsed) + "s"); 135 } 136 137 // Write serialized output 138 System.out.println("Writing " + inv_file + "..."); 139 FileIO.write_serialized_pptmap(result, inv_file); 140 141 System.out.println("Exiting"); 142 } 143 144 /** 145 * Union multiple PptMaps into one. 146 * 147 * @param collector is mutated 148 * @param source is unmodified (but is aliased into) 149 */ 150 public static void union(PptMap collector, PptMap source) { 151 for (PptTopLevel ppt : source.pptIterable()) { 152 153 if ((ppt.numViews() == 0) && (ppt.joiner_view.invs.size() == 0)) { 154 continue; 155 } 156 157 if (collector.get(ppt.ppt_name) != null) { 158 throw new RuntimeException("Cannot merge two non-empty ppts named " + ppt.name()); 159 } 160 161 System.out.println("Adding ppt " + ppt.name()); 162 collector.add(ppt); 163 } 164 } 165}