001// Main routine for Daikon invariant detector
002// For documentation, see file doc/daikon.html in the distribution.
003
004package daikon;
005
006import static java.util.logging.Level.FINE;
007import static java.util.logging.Level.FINER;
008import static java.util.logging.Level.INFO;
009
010import daikon.config.Configuration;
011import daikon.derive.Derivation;
012import daikon.inv.Equality;
013import daikon.inv.Invariant;
014import daikon.inv.OutputFormat;
015import daikon.inv.binary.sequenceScalar.Member;
016import daikon.inv.binary.sequenceScalar.MemberFloat;
017import daikon.inv.binary.sequenceScalar.SeqFloatEqual;
018import daikon.inv.binary.sequenceScalar.SeqFloatGreaterEqual;
019import daikon.inv.binary.sequenceScalar.SeqFloatGreaterThan;
020import daikon.inv.binary.sequenceScalar.SeqFloatLessEqual;
021import daikon.inv.binary.sequenceScalar.SeqFloatLessThan;
022import daikon.inv.binary.sequenceScalar.SeqIntEqual;
023import daikon.inv.binary.sequenceScalar.SeqIntGreaterEqual;
024import daikon.inv.binary.sequenceScalar.SeqIntGreaterThan;
025import daikon.inv.binary.sequenceScalar.SeqIntLessEqual;
026import daikon.inv.binary.sequenceScalar.SeqIntLessThan;
027import daikon.inv.binary.sequenceString.MemberString;
028import daikon.inv.binary.twoScalar.FloatEqual;
029import daikon.inv.binary.twoScalar.FloatGreaterEqual;
030import daikon.inv.binary.twoScalar.FloatGreaterThan;
031import daikon.inv.binary.twoScalar.FloatLessEqual;
032import daikon.inv.binary.twoScalar.FloatLessThan;
033import daikon.inv.binary.twoScalar.FloatNonEqual;
034import daikon.inv.binary.twoScalar.IntEqual;
035import daikon.inv.binary.twoScalar.IntGreaterEqual;
036import daikon.inv.binary.twoScalar.IntGreaterThan;
037import daikon.inv.binary.twoScalar.IntLessEqual;
038import daikon.inv.binary.twoScalar.IntLessThan;
039import daikon.inv.binary.twoScalar.IntNonEqual;
040import daikon.inv.binary.twoScalar.LinearBinary;
041import daikon.inv.binary.twoScalar.LinearBinaryFloat;
042import daikon.inv.binary.twoScalar.NumericFloat;
043import daikon.inv.binary.twoScalar.NumericInt;
044import daikon.inv.binary.twoSequence.PairwiseFloatEqual;
045import daikon.inv.binary.twoSequence.PairwiseFloatGreaterEqual;
046import daikon.inv.binary.twoSequence.PairwiseFloatGreaterThan;
047import daikon.inv.binary.twoSequence.PairwiseFloatLessEqual;
048import daikon.inv.binary.twoSequence.PairwiseFloatLessThan;
049import daikon.inv.binary.twoSequence.PairwiseIntEqual;
050import daikon.inv.binary.twoSequence.PairwiseIntGreaterEqual;
051import daikon.inv.binary.twoSequence.PairwiseIntGreaterThan;
052import daikon.inv.binary.twoSequence.PairwiseIntLessEqual;
053import daikon.inv.binary.twoSequence.PairwiseIntLessThan;
054import daikon.inv.binary.twoSequence.PairwiseLinearBinary;
055import daikon.inv.binary.twoSequence.PairwiseLinearBinaryFloat;
056import daikon.inv.binary.twoSequence.PairwiseNumericFloat;
057import daikon.inv.binary.twoSequence.PairwiseNumericInt;
058import daikon.inv.binary.twoSequence.PairwiseString;
059import daikon.inv.binary.twoSequence.PairwiseStringEqual;
060import daikon.inv.binary.twoSequence.PairwiseStringGreaterEqual;
061import daikon.inv.binary.twoSequence.PairwiseStringGreaterThan;
062import daikon.inv.binary.twoSequence.PairwiseStringLessEqual;
063import daikon.inv.binary.twoSequence.PairwiseStringLessThan;
064import daikon.inv.binary.twoSequence.Reverse;
065import daikon.inv.binary.twoSequence.ReverseFloat;
066import daikon.inv.binary.twoSequence.SeqSeqFloatEqual;
067import daikon.inv.binary.twoSequence.SeqSeqFloatGreaterEqual;
068import daikon.inv.binary.twoSequence.SeqSeqFloatGreaterThan;
069import daikon.inv.binary.twoSequence.SeqSeqFloatLessEqual;
070import daikon.inv.binary.twoSequence.SeqSeqFloatLessThan;
071import daikon.inv.binary.twoSequence.SeqSeqIntEqual;
072import daikon.inv.binary.twoSequence.SeqSeqIntGreaterEqual;
073import daikon.inv.binary.twoSequence.SeqSeqIntGreaterThan;
074import daikon.inv.binary.twoSequence.SeqSeqIntLessEqual;
075import daikon.inv.binary.twoSequence.SeqSeqIntLessThan;
076import daikon.inv.binary.twoSequence.SeqSeqStringEqual;
077import daikon.inv.binary.twoSequence.SeqSeqStringGreaterEqual;
078import daikon.inv.binary.twoSequence.SeqSeqStringGreaterThan;
079import daikon.inv.binary.twoSequence.SeqSeqStringLessEqual;
080import daikon.inv.binary.twoSequence.SeqSeqStringLessThan;
081import daikon.inv.binary.twoSequence.SubSequence;
082import daikon.inv.binary.twoSequence.SubSequenceFloat;
083import daikon.inv.binary.twoSequence.SubSet;
084import daikon.inv.binary.twoSequence.SubSetFloat;
085import daikon.inv.binary.twoSequence.SuperSequence;
086import daikon.inv.binary.twoSequence.SuperSequenceFloat;
087import daikon.inv.binary.twoSequence.SuperSet;
088import daikon.inv.binary.twoSequence.SuperSetFloat;
089import daikon.inv.binary.twoString.StdString;
090import daikon.inv.binary.twoString.StringEqual;
091import daikon.inv.binary.twoString.StringGreaterEqual;
092import daikon.inv.binary.twoString.StringGreaterThan;
093import daikon.inv.binary.twoString.StringLessEqual;
094import daikon.inv.binary.twoString.StringLessThan;
095import daikon.inv.binary.twoString.StringNonEqual;
096import daikon.inv.ternary.threeScalar.FunctionBinary;
097import daikon.inv.ternary.threeScalar.FunctionBinaryFloat;
098import daikon.inv.ternary.threeScalar.LinearTernary;
099import daikon.inv.ternary.threeScalar.LinearTernaryFloat;
100import daikon.inv.unary.scalar.CompleteOneOfScalar;
101import daikon.inv.unary.scalar.IsPointer;
102import daikon.inv.unary.scalar.LowerBound;
103import daikon.inv.unary.scalar.LowerBoundFloat;
104import daikon.inv.unary.scalar.Modulus;
105import daikon.inv.unary.scalar.NonModulus;
106import daikon.inv.unary.scalar.NonZero;
107import daikon.inv.unary.scalar.NonZeroFloat;
108import daikon.inv.unary.scalar.OneOfFloat;
109import daikon.inv.unary.scalar.OneOfScalar;
110import daikon.inv.unary.scalar.RangeFloat;
111import daikon.inv.unary.scalar.RangeInt;
112import daikon.inv.unary.scalar.UpperBound;
113import daikon.inv.unary.scalar.UpperBoundFloat;
114import daikon.inv.unary.sequence.CommonFloatSequence;
115import daikon.inv.unary.sequence.CommonSequence;
116import daikon.inv.unary.sequence.EltLowerBound;
117import daikon.inv.unary.sequence.EltLowerBoundFloat;
118import daikon.inv.unary.sequence.EltNonZero;
119import daikon.inv.unary.sequence.EltNonZeroFloat;
120import daikon.inv.unary.sequence.EltOneOf;
121import daikon.inv.unary.sequence.EltOneOfFloat;
122import daikon.inv.unary.sequence.EltRangeFloat;
123import daikon.inv.unary.sequence.EltRangeInt;
124import daikon.inv.unary.sequence.EltUpperBound;
125import daikon.inv.unary.sequence.EltUpperBoundFloat;
126import daikon.inv.unary.sequence.EltwiseFloatEqual;
127import daikon.inv.unary.sequence.EltwiseFloatGreaterEqual;
128import daikon.inv.unary.sequence.EltwiseFloatGreaterThan;
129import daikon.inv.unary.sequence.EltwiseFloatLessEqual;
130import daikon.inv.unary.sequence.EltwiseFloatLessThan;
131import daikon.inv.unary.sequence.EltwiseIntEqual;
132import daikon.inv.unary.sequence.EltwiseIntGreaterEqual;
133import daikon.inv.unary.sequence.EltwiseIntGreaterThan;
134import daikon.inv.unary.sequence.EltwiseIntLessEqual;
135import daikon.inv.unary.sequence.EltwiseIntLessThan;
136import daikon.inv.unary.sequence.NoDuplicates;
137import daikon.inv.unary.sequence.NoDuplicatesFloat;
138import daikon.inv.unary.sequence.OneOfFloatSequence;
139import daikon.inv.unary.sequence.OneOfSequence;
140import daikon.inv.unary.sequence.SeqIndexFloatEqual;
141import daikon.inv.unary.sequence.SeqIndexFloatGreaterEqual;
142import daikon.inv.unary.sequence.SeqIndexFloatGreaterThan;
143import daikon.inv.unary.sequence.SeqIndexFloatLessEqual;
144import daikon.inv.unary.sequence.SeqIndexFloatLessThan;
145import daikon.inv.unary.sequence.SeqIndexFloatNonEqual;
146import daikon.inv.unary.sequence.SeqIndexIntEqual;
147import daikon.inv.unary.sequence.SeqIndexIntGreaterEqual;
148import daikon.inv.unary.sequence.SeqIndexIntGreaterThan;
149import daikon.inv.unary.sequence.SeqIndexIntLessEqual;
150import daikon.inv.unary.sequence.SeqIndexIntLessThan;
151import daikon.inv.unary.sequence.SeqIndexIntNonEqual;
152import daikon.inv.unary.string.CompleteOneOfString;
153import daikon.inv.unary.string.OneOfString;
154import daikon.inv.unary.string.PrintableString;
155import daikon.inv.unary.stringsequence.CommonStringSequence;
156import daikon.inv.unary.stringsequence.EltOneOfString;
157import daikon.inv.unary.stringsequence.OneOfStringSequence;
158import daikon.simplify.LemmaStack;
159import daikon.split.ContextSplitterFactory;
160import daikon.split.PptSplitter;
161import daikon.split.SpinfoFile;
162import daikon.split.Splitter;
163import daikon.split.SplitterFactory;
164import daikon.split.SplitterList;
165import daikon.suppress.NIS;
166import daikon.suppress.NIS.SuppressionProcessor;
167import gnu.getopt.Getopt;
168import gnu.getopt.LongOpt;
169import java.io.File;
170import java.io.FileInputStream;
171import java.io.IOException;
172import java.io.InputStream;
173import java.io.LineNumberReader;
174import java.lang.reflect.Field;
175import java.lang.reflect.Method;
176import java.time.LocalDateTime;
177import java.time.ZoneId;
178import java.util.ArrayList;
179import java.util.Collection;
180import java.util.ConcurrentModificationException;
181import java.util.HashSet;
182import java.util.Iterator;
183import java.util.LinkedHashMap;
184import java.util.LinkedHashSet;
185import java.util.List;
186import java.util.Map;
187import java.util.Set;
188import java.util.TreeSet;
189import java.util.concurrent.TimeUnit;
190import java.util.logging.Logger;
191import java.util.regex.Matcher;
192import java.util.regex.Pattern;
193import java.util.regex.PatternSyntaxException;
194import org.checkerframework.checker.interning.qual.Interned;
195import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
196import org.checkerframework.checker.nullness.qual.KeyFor;
197import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
198import org.checkerframework.checker.nullness.qual.NonNull;
199import org.checkerframework.checker.nullness.qual.Nullable;
200import org.checkerframework.checker.nullness.qual.RequiresNonNull;
201import org.checkerframework.checker.signature.qual.ClassGetName;
202import org.checkerframework.dataflow.qual.Pure;
203import org.plumelib.util.EntryReader;
204import org.plumelib.util.FilesPlume;
205import org.plumelib.util.RegexUtil;
206import org.plumelib.util.StringsPlume;
207import typequals.prototype.qual.Prototype;
208
209/**
210 * The {@link #main} method is the main entry point for the Daikon invariant detector. The {@link
211 * #mainHelper} method is the entry point, when called programmatically.
212 */
213public final class Daikon {
214
215  private Daikon() {
216    throw new Error("do not instantiate");
217  }
218
219  /**
220   * The amount of time to wait between updates of the progress display, measured in milliseconds. A
221   * value of -1 means do not print the progress display at all.
222   */
223  public static int dkconfig_progress_delay = 1000;
224
225  /** The current version of Daikon. */
226  public static final String release_version = "5.8.22";
227
228  /** The date for the current version of Daikon. */
229  public static final String release_date = "June 3, 2025";
230
231  /** A description of the Daikon release (version number, date, and URL). */
232  public static final String release_string =
233      "Daikon version "
234          + release_version
235          + ", released "
236          + release_date
237          + "; http://plse.cs.washington.edu/daikon.";
238
239  // Variables starting with dkconfig_ should only be set via the
240  // daikon.config.Configuration interface.
241  /** Boolean. Controls whether conditional program points are displayed. */
242  public static boolean dkconfig_output_conditionals = true;
243
244  // Variables starting with dkconfig_ should only be set via the
245  // daikon.config.Configuration interface.
246  /** Boolean. Just print the total number of possible invariants and exit. */
247  public static boolean dkconfig_calc_possible_invs;
248
249  /**
250   * Integer. Percentage of program points to process. All program points are sorted by name, and
251   * all samples for the first {@code ppt_perc} program points are processed. A percentage of 100
252   * matches all program points.
253   */
254  public static int dkconfig_ppt_perc = 100;
255
256  /**
257   * Boolean. Controls whether or not the total samples read and processed are printed at the end of
258   * processing.
259   */
260  public static boolean dkconfig_print_sample_totals = false;
261
262  // All these variables really need to be organized better.
263
264  public static final String lineSep = Global.lineSep;
265
266  /**
267   * Boolean. Controls whether or not processing information is printed out. Setting this variable
268   * to true also automatically sets {@code progress_delay} to -1.
269   */
270  public static boolean dkconfig_quiet = false;
271
272  // Change this at your peril; high costs in time and space for "false",
273  // because so many more invariants get instantiated.
274  public static final boolean check_program_types = true;
275
276  // Problem with setting this to true:
277  //  get no invariants over any value that can ever be missing
278  // Problem with setting this to false:
279  //  due to different number of samples, isEqualityComparison is
280  //  non-transitive (that is specially handled in the code)
281  public static final boolean invariants_check_canBeMissing = false;
282
283  // Specialized version for array elements; only examined if
284  // invariants_check_canBeMissing is false
285  public static final boolean invariants_check_canBeMissing_arrayelt = true;
286
287  public static final boolean disable_modbit_check_message = false;
288
289  /**
290   * Not a good idea to set this to true, as it is too easy to ignore the warnings and the modbit
291   * problem can cause an error later.
292   */
293  public static final boolean disable_modbit_check_error = false;
294
295  /** When true, don't print textual output. */
296  public static boolean no_text_output = false;
297
298  /**
299   * When true, show how much time each Daikon phase took. Has no effect if System.console() is not
300   * connected to a terminal. If option show-detail-progress was used, also show how much time each
301   * program point took.
302   */
303  public static boolean show_progress = false;
304
305  /**
306   * Whether to use the "new" equality set mechanism for handling equality, using canonicals to have
307   * instantiation of invariants only over equality sets.
308   */
309  public static boolean use_equality_optimization = true;
310
311  /**
312   * Boolean. Controls whether the Daikon optimizations (equality sets, suppressions) are undone at
313   * the end to create a more complete set of invariants. Output does not include conditional
314   * program points, implications, reflexive and partially reflexive invariants.
315   */
316  public static boolean dkconfig_undo_opts = false;
317
318  /**
319   * Boolean. Indicates to Daikon classes and methods that the methods calls should be compatible to
320   * DaikonSimple because Daikon and DaikonSimple share methods. Default value is 'false'.
321   */
322  public static boolean using_DaikonSimple = false;
323
324  /**
325   * If "always", then invariants are always guarded. If "never", then invariants are never guarded.
326   * If "missing", then invariants are guarded only for variables that were missing ("can be
327   * missing") in the dtrace (the observed executions). If "default", then use "missing" mode for
328   * Java output and "never" mode otherwise.
329   *
330   * <p>Guarding means adding predicates that ensure that variables can be dereferenced. For
331   * instance, if {@code a} can be null --- that is, if {@code a.b} can be nonsensical --- then the
332   * guarded version of
333   *
334   * <pre>a.b == 5</pre>
335   *
336   * is
337   *
338   * <pre>(a != null) &rArr; (a.b == 5)</pre>
339   *
340   * .
341   *
342   * <p>(To do: Some configuration option (maybe this one) should add guards for other reasons that
343   * lead to nonsensical values (@pxref{Variable names}).) <br>
344   * &#64;cindex nonsensical values for variables, guarding
345   */
346  // Perhaps a better default would be "missing".
347  public static @Interned String dkconfig_guardNulls = "default";
348
349  /**
350   * Whether to associate the program points in a dataflow hierarchy, as via Nimmer's thesis.
351   * Deactivate only for languages and analyses where flow relation is nonsensical.
352   */
353  public static boolean use_dataflow_hierarchy = true;
354
355  /**
356   * Whether to use the bottom up implementation of the dataflow hierarchy. This mechanism builds
357   * invariants initially only at the leaves of the partial order. Upper points are calculated by
358   * joining the invariants from each of their children points.
359   */
360  // public static boolean dkconfig_df_bottom_up = true;
361
362  // When true, use the Simplify theorem prover (not part of Daikon)
363  // to locate logically redundant invariants, and flag them as
364  // redundant, so that they are removed from the printed output.
365  public static boolean suppress_redundant_invariants_with_simplify = false;
366
367  public static OutputFormat output_format = OutputFormat.DAIKON;
368
369  // When true, output numbers of values and samples (also names of variables)
370  public static boolean output_num_samples = false;
371
372  public static boolean ignore_comparability = false;
373
374  // Controls which program points/variables are used/ignored.
375  public static @Nullable Pattern ppt_regexp;
376  public static @Nullable Pattern ppt_omit_regexp;
377  public static @Nullable Pattern var_regexp;
378  public static @Nullable Pattern var_omit_regexp;
379
380  /**
381   * If set, only ppts less than ppt_max_name are included. Used by the configuration option
382   * dkconfig_ppt_percent to only work on a specified percent of the ppts.
383   */
384  public static @Nullable String ppt_max_name = null;
385
386  // The invariants detected will be serialized and written to this
387  // file.
388  public static @Nullable File inv_file;
389
390  // Whether we want the memory monitor activated
391  private static boolean use_mem_monitor = false;
392
393  /** Whether Daikon should print its version number and date. */
394  public static boolean noversion_output = false;
395
396  /** Whether Daikon is in its inferencing loop. Used only for assertion checks. */
397  public static boolean isInferencing = false;
398
399  /**
400   * When true, omit certain invariants from the output {@code .inv} file. Generally these are
401   * invariants that wouldn't be printed in any case; but by default, they're retained in the {@code
402   * .inv} file in case they would be useful for later processing. (For instance, we might at some
403   * point in the future support resuming processing with more data from an {@code .inv} file).
404   * These invariants can increase the size of the {@code .inv} file, though, so when only limited
405   * further processing is needed, it can save space to omit them.
406   */
407  public static boolean omit_from_output = false;
408
409  /**
410   * An array of flags, indexed by characters, in which a true entry means that invariants of that
411   * sort should be omitted from the output {@code .inv} file.
412   */
413  public static boolean[] omit_types = new boolean[256];
414
415  // Command-line options / command-line arguments
416  // These variables are public so other programs can reuse the same
417  // command-line options.
418
419  /** option help. */
420  public static final String help_SWITCH = "help";
421
422  // "-o" switch: file to which serialized output is written
423  /** option no-text-output. */
424  public static final String no_text_output_SWITCH = "no_text_output";
425
426  /** option format. */
427  public static final String format_SWITCH = "format";
428
429  /** option show-progress. */
430  public static final String show_progress_SWITCH = "show_progress";
431
432  /** option show-detail-progress. */
433  public static final String show_detail_progress_SWITCH = "show_detail_progress";
434
435  /** option no-show-progress. */
436  public static final String no_show_progress_SWITCH = "no_show_progress";
437
438  /** option noversion. */
439  public static final String noversion_SWITCH = "noversion";
440
441  public static final String output_num_samples_SWITCH = "output_num_samples";
442  public static final String files_from_SWITCH = "files_from";
443  public static final String omit_from_output_SWITCH = "omit_from_output";
444  // Control invariant detection
445  public static final String conf_limit_SWITCH = "conf_limit";
446  public static final String list_type_SWITCH = "list_type";
447  public static final String user_defined_invariant_SWITCH = "user-defined-invariant";
448  public static final String disable_all_invariants_SWITCH = "disable-all-invariants";
449  public static final String no_dataflow_hierarchy_SWITCH = "nohierarchy";
450  public static final String suppress_redundant_SWITCH = "suppress_redundant";
451  // Process only part of the trace file
452  public static final String ppt_regexp_SWITCH = "ppt-select-pattern";
453  public static final String ppt_omit_regexp_SWITCH = "ppt-omit-pattern";
454  public static final String var_regexp_SWITCH = "var-select-pattern";
455  public static final String var_omit_regexp_SWITCH = "var-omit-pattern";
456  // Configuration options
457  public static final String server_SWITCH =
458      "server"; // YOAV: server mode for Daikon: reads dtrace files as they appear
459  public static final String config_SWITCH = "config";
460  public static final String config_option_SWITCH = "config_option";
461  // Debugging
462  public static final String debugAll_SWITCH = "debug";
463  public static final String debug_SWITCH = "dbg";
464  public static final String track_SWITCH = "track";
465  public static final String disc_reason_SWITCH = "disc_reason";
466  public static final String mem_stat_SWITCH = "mem_stat";
467  public static final String wrap_xml_SWITCH = "wrap_xml";
468
469  /**
470   * Regular expression that matches class names in the format expected by {@link
471   * Class#forName(String)}.
472   */
473  // This regular expression is taken from
474  // checker-framework/checker/src/org/checkerframework/checker/signature/qual/ClassGetName.java
475  // .  It's a bit too lenient since we don't need to permit arrays and
476  // primitives.
477  private static final String classGetNameRegex =
478      "(^[A-Za-z_][A-Za-z_0-9]*(\\.[A-Za-z_][A-Za-z_0-9]*)*(\\$[A-Za-z_0-9]+)*$)|^\\[+([BCDFIJSZ]|L[A-Za-z_][A-Za-z_0-9]*(\\.[A-Za-z_][A-Za-z_0-9]*)*(\\$[A-Za-z_0-9]+)*;)$";
479
480  private static final Pattern classGetNamePattern;
481
482  static {
483    try {
484      classGetNamePattern = Pattern.compile(classGetNameRegex);
485    } catch (PatternSyntaxException e) {
486      // This shouldn't happen because classGetNameRegex is a legal regex
487      throw new Error(e);
488    }
489  }
490
491  public static @MonotonicNonNull File server_dir =
492      null; // YOAV: the directory from which we read the dtrace files
493
494  /**
495   * A PptMap (mapping from String to PptTopLevel) that contains all the program points. Set in
496   * mainHelper().
497   */
498  @SuppressWarnings("nullness:initialization.static.field.uninitialized") // set in mainHelper()
499  public static PptMap all_ppts;
500
501  /** current invariant (used for debugging) */
502  public static @Nullable Invariant current_inv = null;
503
504  /* List of prototype invariants (one for each type of invariant) */
505  public static ArrayList<@Prototype Invariant> proto_invs = new ArrayList<>();
506
507  /** Debug tracer. */
508  public static final Logger debugTrace = Logger.getLogger("daikon.Daikon");
509
510  public static final Logger debugProgress = Logger.getLogger("daikon.Progress");
511
512  public static final Logger debugEquality = Logger.getLogger("daikon.Equality");
513
514  /** Debug tracer for ppt initialization. */
515  public static final Logger debugInit = Logger.getLogger("daikon.init");
516
517  /** Prints out statistics concerning equality sets, suppressions, etc. */
518  public static final Logger debugStats = Logger.getLogger("daikon.stats");
519
520  /** The usage message for this program. */
521  static String usage =
522      StringsPlume.joinLines(
523          release_string,
524          // "Uses the Java port of GNU getopt, copyright (c) 1998 Aaron M. Renn",
525          // "For licensing information, see the License section of the manual.",
526          "Usage:",
527          "    java daikon.Daikon [flags...] files...",
528          "  Each file is a declaration file or a data trace file; the file type",
529          "  is determined by the file name (containing \".decls\" or \".dtrace\").",
530          "  For a list of flags, see the Daikon manual, which appears in the ",
531          "  Daikon distribution and also at http://plse.cs.washington.edu/daikon/.");
532
533  /** Indicates the need for Daikon to terminate. */
534  public abstract static class DaikonTerminationException extends RuntimeException {
535    static final long serialVersionUID = 20180729L;
536
537    protected DaikonTerminationException() {
538      super();
539    }
540
541    protected DaikonTerminationException(String message) {
542      super(message);
543    }
544
545    protected DaikonTerminationException(Throwable e) {
546      super(e);
547    }
548
549    protected DaikonTerminationException(String message, Throwable e) {
550      super(message, e);
551    }
552  }
553
554  /** Indicates that Daikon has terminated normally. */
555  public static class NormalTermination extends DaikonTerminationException {
556    static final long serialVersionUID = 20180729L;
557
558    public NormalTermination(String message) {
559      super(message);
560    }
561
562    public NormalTermination() {
563      super();
564    }
565  }
566
567  /**
568   * Indicates that Daikon has terminated abnormally, indicating a bug in Daikon. Also used to
569   * indicate a situation that should not happen.
570   */
571  public static class BugInDaikon extends DaikonTerminationException {
572    static final long serialVersionUID = 20180729L;
573
574    public BugInDaikon(String message) {
575      super(message);
576    }
577
578    public BugInDaikon(Throwable e) {
579      super(e);
580    }
581
582    public BugInDaikon(String message, Throwable e) {
583      super(message, e);
584    }
585  }
586
587  /**
588   * Indicates a user error. Thrown to indicate that main should not print a stack trace, but only
589   * print the message itself to the user.
590   *
591   * <p>Code in Daikon should throw other exceptions such as BugInDaikon in cases of a Daikon bug or
592   * a system problem (like unpredictable IOExceptions). If the string is null, then this is normal
593   * termination, not an error; no message is printed.
594   */
595  public static class UserError extends DaikonTerminationException {
596    static final long serialVersionUID = 20050923L;
597
598    public static String error_at_line_file(LineNumberReader reader, String filename, Throwable e) {
599      String msg = e.getMessage();
600      if (msg == null) {
601        msg = " of type " + e.getClass() + " with no detail message";
602      }
603      return error_at_line_file(reader, filename, msg);
604    }
605
606    public static String error_at_line_file(LineNumberReader reader, String filename, String msg) {
607      if (msg == null) {
608        throw new Error("Null message supplied to error_at_line_file()");
609      }
610      return "Error at line " + reader.getLineNumber() + " in file " + filename + ": " + msg;
611    }
612
613    // Constructors that take a Throwable
614
615    // Requires that e.getMessage() != null.
616    public UserError(Throwable e) {
617      super(e.getMessage() == null ? e.toString() : e.getMessage(), e);
618    }
619
620    public UserError(Throwable e, String msg) {
621      super(
622          ((e.getMessage() != null && msg.contains(e.getMessage()))
623              ? msg
624              : msg + ": " + e.getMessage()),
625          e);
626    }
627
628    public UserError(Throwable e, FileIO.ParseState state) {
629      this(e, error_at_line_file(state.reader, state.filename, e));
630    }
631
632    public UserError(Throwable e, LineNumberReader reader, String filename) {
633      this(e, error_at_line_file(reader, filename, e));
634    }
635
636    public UserError(Throwable e, String msg, LineNumberReader reader, String filename) {
637      this(e, error_at_line_file(reader, filename, msg));
638    }
639
640    // Constructors that do not take a Throwable
641
642    /** Creates a UserError with no details. */
643    public UserError() {
644      super("");
645    }
646
647    public UserError(String s) {
648      super(s);
649    }
650
651    public UserError(String msg, FileIO.ParseState state) {
652      super(error_at_line_file(state.reader, state.filename, msg));
653    }
654
655    public UserError(String msg, LineNumberReader reader, String filename) {
656      super(error_at_line_file(reader, filename, msg));
657    }
658    // This constructor is too error-prone:  it leads to throwing away
659    // subsequent args if there are not enough % directives in the string.
660    // public UserError(String format, @Nullable Object... args) {
661    //   super (String.format (format, args));
662    // }
663  }
664
665  /** A parser error that should be reported, with better context, by the caller. */
666  public static class ParseError extends Exception {
667    static final long serialVersionUID = 20181021L;
668
669    ParseError(String s) {
670      super(s);
671    }
672  }
673
674  /**
675   * The arguments to daikon.Daikon are file names. Declaration file names end in ".decls", and data
676   * trace file names end in ".dtrace".
677   */
678  public static void main(final String[] args) {
679    try {
680      mainHelper(args);
681    } catch (DaikonTerminationException e) {
682      handleDaikonTerminationException(e);
683    }
684  }
685
686  /**
687   * Handle DaikonTerminationExceptions. Others are left to be handled by the default handler, which
688   * prints a more informative stack trace.
689   */
690  public static void handleDaikonTerminationException(DaikonTerminationException e) {
691    if (e instanceof NormalTermination) {
692      System.out.println();
693      if (e.getMessage() != null) {
694        System.out.println(e.getMessage());
695      }
696      System.exit(0);
697    } else if (e instanceof UserError) {
698      System.err.println();
699      System.err.println(e.getMessage());
700      if (Debug.dkconfig_show_stack_trace) {
701        e.printStackTrace();
702      }
703      System.exit(1);
704    } else if (e instanceof BugInDaikon) {
705      System.err.println();
706      System.err.println("Bug in Daikon.  Please report.");
707      e.printStackTrace(System.err);
708      System.err.println("Bug in Daikon.  Please report.");
709      System.exit(2);
710    } else {
711      // This case should never be executed.
712      System.err.println();
713      System.err.println("Unknown problem in Daikon.  Please report.");
714      e.printStackTrace(System.err);
715      System.err.println("Unknown problem in Daikon.  Please report.");
716      System.exit(2);
717    }
718  }
719
720  /**
721   * This does the work of {@link #main}, but it never calls System.exit, so it is appropriate to be
722   * called progrmmatically.
723   *
724   * @param args the command-line arguments
725   */
726  @SuppressWarnings({
727    "nullness:contracts.precondition", // private field
728    "SystemConsoleNull" // https://errorprone.info/bugpattern/SystemConsoleNull
729  })
730  public static void mainHelper(final String[] args) {
731    long startTime = System.nanoTime();
732    long duration;
733
734    // Cleanup from any previous runs
735    cleanup();
736
737    // Read command line options
738    FileOptions files = read_options(args, usage);
739    Set<File> decls_files = files.decls;
740    Set<String> dtrace_files = files.dtrace;
741    Set<File> spinfo_files = files.spinfo;
742    Set<File> map_files = files.map;
743    if (server_dir == null && (decls_files.size() == 0) && (dtrace_files.size() == 0)) {
744      System.out.println("No .decls or .dtrace files specified");
745      throw new Daikon.UserError("No .decls or .dtrace files specified");
746    }
747
748    // Never disable splitting for csharp format.
749    if (Daikon.dkconfig_undo_opts && Daikon.output_format != OutputFormat.CSHARPCONTRACT) {
750      PptSplitter.dkconfig_disable_splitting = true;
751    }
752
753    if (Daikon.dkconfig_quiet) {
754      Daikon.dkconfig_progress_delay = -1;
755    }
756    if (System.console() == null && !show_progress) {
757      // not connected to a terminal
758      Daikon.dkconfig_progress_delay = -1;
759    }
760
761    // Set up debug traces; note this comes after reading command line options.
762    LogHelper.setupLogs(Global.debugAll ? FINE : INFO);
763
764    if (!noversion_output) {
765      if (!Daikon.dkconfig_quiet) {
766        System.out.println(release_string);
767      }
768    }
769
770    // figure out which algorithm to use in NIS to process suppressions
771    if (NIS.dkconfig_suppression_processor == SuppressionProcessor.HYBRID) {
772      NIS.hybrid_method = true;
773    } else {
774      if (NIS.dkconfig_suppression_processor == SuppressionProcessor.ANTECEDENT) {
775        NIS.antecedent_method = true;
776        NIS.hybrid_method = false;
777      } else {
778        assert (NIS.dkconfig_suppression_processor == SuppressionProcessor.FALSIFIED);
779        NIS.antecedent_method = false;
780        NIS.hybrid_method = false;
781      }
782    }
783
784    // Create the list of all invariant types
785    setup_proto_invs();
786
787    if (PrintInvariants.print_discarded_invariants) {
788      DiscReasonMap.initialize();
789    }
790
791    fileio_progress = new FileIOProgress();
792    fileio_progress.start();
793
794    // Load declarations and splitters
795    load_spinfo_files(spinfo_files);
796    all_ppts = load_decls_files(decls_files);
797    load_map_files(map_files);
798
799    all_ppts.trimToSize();
800
801    // Only for assertion checks
802    isInferencing = true;
803
804    // Infer invariants
805    process_data(all_ppts, dtrace_files);
806    isInferencing = false;
807    if (Debug.logOn()) {
808      Debug.check(all_ppts, "After process data");
809    }
810
811    // If requested, just calculate the total number of invariants possible
812    if (dkconfig_calc_possible_invs) {
813      fileio_progress.shouldStop = true;
814      int total_invs = 0;
815      // Can't use new for syntax because the default iterator for all_ppts
816      // is not the one I want here.
817      for (PptTopLevel ppt : all_ppts.ppt_all_iterable()) {
818        System.out.printf("Processing %s with %d variables", ppt.name(), ppt.var_infos.length);
819        int inv_cnt = 0;
820        if (ppt.var_infos.length > 1600) {
821          System.out.println("Skipping, too many variables!");
822        } else {
823          inv_cnt = ppt.getInvariants().size();
824          System.out.println(" " + inv_cnt + " invariants");
825          total_invs += inv_cnt;
826        }
827      }
828      System.out.println(total_invs + " invariants total");
829      return;
830    }
831
832    if (suppress_redundant_invariants_with_simplify) {
833      suppressWithSimplify(all_ppts);
834    }
835
836    // Check that PptMap created was correct
837    all_ppts.repCheck();
838
839    // Remove undesired invariants, if requested
840    if (omit_from_output) {
841      processOmissions(all_ppts);
842    }
843
844    debugProgress.fine(" Writing Serialized Pptmap ... ");
845    long startWriteTime = System.nanoTime();
846    // Write serialized output - must be done before guarding invariants
847    if (inv_file != null) {
848      try {
849        FileIO.write_serialized_pptmap(all_ppts, inv_file);
850      } catch (IOException e) {
851        throw new RuntimeException("Error while writing .inv file: " + inv_file, e);
852      }
853    }
854    duration = System.nanoTime() - startWriteTime;
855    debugProgress.fine(
856        " Writing Serialized Pptmap ... done [" + TimeUnit.NANOSECONDS.toSeconds(duration) + "]");
857
858    //     if ((Daikon.dkconfig_guardNulls == "always") // interned
859    //         || (Daikon.dkconfig_guardNulls == "missing")) { // interned
860    //       // This side-effects the PptMap, but it has already been saved
861    //       // to disk and is now being used only for printing.
862    //       guardInvariants(all_ppts);
863    //     }
864
865    // Debug print information about the variables
866    if (false) {
867      for (PptTopLevel ppt : all_ppts.all_ppts()) {
868        System.out.printf("Dumping variables for ppt %s%n", ppt.name());
869        for (VarInfo vi : ppt.var_infos) {
870          System.out.printf("  vi %s%n", vi);
871          System.out.printf("    file_rep_type = %s%n", vi.file_rep_type);
872          System.out.printf("    type = %s%n", vi.type);
873        }
874      }
875    }
876
877    // print out the invariants for each program point
878    if (Daikon.dkconfig_undo_opts) {
879      // Print out the invariants for each program point (sort first)
880      for (PptTopLevel ppt : all_ppts.pptIterable()) {
881
882        // We do not need to print out program points that have not seen
883        // any samples.
884        if (ppt.num_samples() == 0) {
885          continue;
886        }
887        List<Invariant> invs = PrintInvariants.sort_invariant_list(ppt.invariants_vector());
888        List<Invariant> filtered_invs = filter_invs(invs);
889
890        // Debugging output.  Sometimes the program points actually differ in number of samples seen
891        // due to differences in how Daikon and DaikonSimple see the variable hierarchy.
892        if (false) {
893          System.out.println("====================================================");
894          System.out.println(ppt.name());
895          System.out.println(ppt.num_samples());
896
897          for (Invariant inv : filtered_invs) {
898            System.out.println(inv.getClass());
899            System.out.println(inv);
900          }
901        }
902      }
903
904      // exit the program
905      if (false) {
906        return;
907      }
908    }
909
910    // Display invariants
911    if (output_num_samples) {
912      System.out.println("The --output_num_samples debugging flag is on.");
913      System.out.println("Some of the debugging output may only make sense to Daikon programmers.");
914    }
915
916    // If they want to see discarded invariants, they probably don't
917    // want to see the true ones.
918    if (!PrintInvariants.print_discarded_invariants) {
919      PrintInvariants.print_invariants(all_ppts);
920    } else {
921      PrintInvariants.print_reasons(all_ppts);
922    }
923
924    if (output_num_samples) {
925      Global.output_statistics();
926    }
927    if (dkconfig_print_sample_totals) {
928      System.out.println(FileIO.samples_processed + " samples processed");
929    }
930
931    // print statistics concerning what invariants are printed
932    if (debugStats.isLoggable(FINE)) {
933      for (PptTopLevel ppt : all_ppts.ppt_all_iterable()) {
934        PrintInvariants.print_filter_stats(debugStats, ppt, all_ppts);
935      }
936    }
937
938    duration = System.nanoTime() - startTime;
939    debugProgress.fine(" Total time spent in Daikon: " + TimeUnit.NANOSECONDS.toSeconds(duration));
940
941    // Done
942    if (!Daikon.dkconfig_quiet) {
943      System.out.println("Exiting Daikon.");
944    }
945  }
946
947  /** Cleans up static variables so that mainHelper can be called more than once. */
948  @SuppressWarnings("nullness") // reinitialization
949  public static void cleanup() {
950
951    // Stop the thread that prints out progress information
952    if ((fileio_progress != null) && (fileio_progress.getState() != Thread.State.NEW)) {
953      fileio_progress.shouldStop = true;
954      try {
955        fileio_progress.join(2000);
956      } catch (InterruptedException e) {
957        // It's OK for a wait operation to be interrupted.
958      }
959      if (fileio_progress.getState() != Thread.State.TERMINATED) {
960        throw new BugInDaikon("Can't stop fileio_progress thead");
961      }
962    }
963    fileio_progress = null;
964    progress = "";
965
966    // Reset statics.  Unfortunately, these must match the settings where
967    // these are declared and I don't know how to do that automatically.
968    inv_file = null;
969    no_text_output = false;
970    show_progress = false;
971    output_format = OutputFormat.DAIKON;
972    noversion_output = false;
973    output_num_samples = false;
974    omit_from_output = false;
975    omit_types = new boolean[256];
976    use_dataflow_hierarchy = true;
977    suppress_redundant_invariants_with_simplify = false;
978    ppt_regexp = null;
979    ppt_omit_regexp = null;
980    var_regexp = null;
981    var_omit_regexp = null;
982    server_dir = null;
983    use_mem_monitor = false;
984
985    proto_invs.clear();
986  }
987
988  // Structure for return value of read_options.
989  // Return an array of {decls, dtrace, spinfo, map} files.
990  public static class FileOptions {
991    public Set<File> decls;
992    public Set<String> dtrace;
993    public Set<File> spinfo;
994    public Set<File> map;
995
996    public FileOptions(Set<File> decls, Set<String> dtrace, Set<File> spinfo, Set<File> map) {
997      this.decls = decls;
998      this.dtrace = dtrace;
999      this.spinfo = spinfo;
1000      this.map = map;
1001    }
1002  }
1003
1004  // ///////////////////////////////////////////////////////////////////////////
1005  // Read in the command line options
1006  // Return {decls, dtrace, spinfo, map} files.
1007  static FileOptions read_options(String[] args, String usage) {
1008    if (args.length == 0) {
1009      System.out.println("Error: no files supplied on command line.");
1010      System.out.println(usage);
1011      throw new Daikon.UserError();
1012    }
1013
1014    // LinkedHashSet because it can be confusing to users if files (of the
1015    // same type) are gratuitously processed in a different order than they
1016    // were supplied on the command line.
1017    HashSet<File> decl_files = new LinkedHashSet<>();
1018    HashSet<String> dtrace_files = new LinkedHashSet<>(); // file names or "-" or "+"
1019    HashSet<File> spinfo_files = new LinkedHashSet<>();
1020    HashSet<File> map_files = new LinkedHashSet<>();
1021
1022    LongOpt[] longopts =
1023        new LongOpt[] {
1024          // Control output
1025          new LongOpt(help_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
1026          new LongOpt(no_text_output_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
1027          new LongOpt(format_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
1028          new LongOpt(show_progress_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
1029          new LongOpt(show_detail_progress_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
1030          new LongOpt(no_show_progress_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
1031          new LongOpt(noversion_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
1032          new LongOpt(output_num_samples_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
1033          new LongOpt(files_from_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
1034          new LongOpt(omit_from_output_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
1035          // Control invariant detection
1036          new LongOpt(conf_limit_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
1037          new LongOpt(list_type_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
1038          new LongOpt(user_defined_invariant_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
1039          new LongOpt(disable_all_invariants_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
1040          new LongOpt(no_dataflow_hierarchy_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
1041          new LongOpt(suppress_redundant_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
1042          // Process only part of the trace file
1043          new LongOpt(ppt_regexp_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
1044          new LongOpt(ppt_omit_regexp_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
1045          new LongOpt(var_regexp_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
1046          new LongOpt(var_omit_regexp_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
1047          // Configuration options
1048          new LongOpt(server_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
1049          new LongOpt(config_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
1050          new LongOpt(config_option_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
1051          // Debugging
1052          new LongOpt(debugAll_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
1053          new LongOpt(debug_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
1054          new LongOpt(track_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
1055          new LongOpt(disc_reason_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
1056          new LongOpt(mem_stat_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
1057        };
1058    Getopt g = new Getopt("daikon.Daikon", args, "ho:", longopts);
1059    int c;
1060
1061    while ((c = g.getopt()) != -1) {
1062      switch (c) {
1063        case 0:
1064          // got a long option
1065          String option_name = longopts[g.getLongind()].getName();
1066
1067          // Control output
1068          if (help_SWITCH.equals(option_name)) {
1069            System.out.println(usage);
1070            throw new Daikon.NormalTermination();
1071          } else if (no_text_output_SWITCH.equals(option_name)) {
1072            no_text_output = true;
1073          } else if (format_SWITCH.equals(option_name)) {
1074            String format_name = getOptarg(g);
1075            Daikon.output_format = OutputFormat.get(format_name);
1076            if (Daikon.output_format == null) {
1077              throw new Daikon.UserError("Unknown output format:  --format " + format_name);
1078            }
1079          } else if (show_progress_SWITCH.equals(option_name)) {
1080            show_progress = true;
1081            LogHelper.setLevel("daikon.Progress", FINE);
1082          } else if (show_detail_progress_SWITCH.equals(option_name)) {
1083            show_progress = true;
1084            LogHelper.setLevel("daikon.Progress", FINER);
1085          } else if (no_show_progress_SWITCH.equals(option_name)) {
1086            show_progress = false;
1087          } else if (noversion_SWITCH.equals(option_name)) {
1088            noversion_output = true;
1089          } else if (output_num_samples_SWITCH.equals(option_name)) {
1090            output_num_samples = true;
1091          } else if (files_from_SWITCH.equals(option_name)) {
1092            String files_from_filename = getOptarg(g);
1093            try (EntryReader entryReader = new EntryReader(files_from_filename)) {
1094              for (String filename : entryReader) {
1095                // Ignore blank lines in file.
1096                if (filename.equals("")) {
1097                  continue;
1098                }
1099                // This code is duplicated below outside the options loop.
1100                // These aren't "endsWith()" because there might be a suffix
1101                // on the end (eg, a date, or ".gz").
1102                File file = new File(filename);
1103                if (!file.exists()) {
1104                  throw new Daikon.UserError("File " + filename + " not found.");
1105                }
1106                if (filename.indexOf(".decls") != -1) {
1107                  decl_files.add(file);
1108                } else if (filename.indexOf(".dtrace") != -1) {
1109                  dtrace_files.add(filename);
1110                } else if (filename.indexOf(".spinfo") != -1) {
1111                  spinfo_files.add(file);
1112                } else if (filename.indexOf(".map") != -1) {
1113                  map_files.add(file);
1114                } else {
1115                  throw new Daikon.UserError("Unrecognized file extension: " + filename);
1116                }
1117              }
1118            } catch (IOException e) {
1119              throw new RuntimeException(
1120                  String.format("Error reading --files_from file: %s", files_from_filename));
1121            }
1122            break;
1123          } else if (omit_from_output_SWITCH.equals(option_name)) {
1124            String f = getOptarg(g);
1125            for (int i = 0; i < f.length(); i++) {
1126              if ("0rs".indexOf(f.charAt(i)) == -1) {
1127                throw new Daikon.UserError(
1128                    "omit_from_output flag letter '" + f.charAt(i) + "' is unknown");
1129              }
1130              omit_types[f.charAt(i)] = true;
1131            }
1132            omit_from_output = true;
1133          }
1134          // Control invariant detection
1135          else if (conf_limit_SWITCH.equals(option_name)) {
1136            double limit = Double.parseDouble(getOptarg(g));
1137            if ((limit < 0.0) || (limit > 1.0)) {
1138              throw new Daikon.UserError(conf_limit_SWITCH + " must be between [0..1]");
1139            }
1140            Configuration.getInstance()
1141                .apply("daikon.inv.Invariant.confidence_limit", String.valueOf(limit));
1142          } else if (list_type_SWITCH.equals(option_name)) {
1143            try {
1144              String list_type_string = getOptarg(g);
1145              ProglangType.list_implementors.add(list_type_string);
1146            } catch (Exception e) {
1147              throw new Daikon.UserError("Problem parsing " + list_type_SWITCH + " option: " + e);
1148            }
1149            break;
1150          } else if (user_defined_invariant_SWITCH.equals(option_name)) {
1151            try {
1152              String user_defined_invariant_string = getOptarg(g);
1153              Matcher m = classGetNamePattern.matcher(user_defined_invariant_string);
1154              if (!m.matches()) {
1155                throw new Daikon.UserError(
1156                    "Bad argument "
1157                        + user_defined_invariant_string
1158                        + " for "
1159                        + ppt_regexp_SWITCH
1160                        + ": not in the format required by Class.getName(String)");
1161              }
1162              @SuppressWarnings("signature") // Regex match guarantees the format of Class.getName()
1163              @ClassGetName String cname = user_defined_invariant_string;
1164              userDefinedInvariants.add(cname);
1165            } catch (Exception e) {
1166              throw new Daikon.UserError(
1167                  "Problem parsing " + user_defined_invariant_SWITCH + " option: " + e);
1168            }
1169            break;
1170          } else if (disable_all_invariants_SWITCH.equals(option_name)) {
1171            // There are two possibilities:
1172            //  * a given invariant class is not yet loaded, in which case
1173            //    we set the default value for its dkconfig_enabled field.
1174            //  * a given invariant class is already loaded, in which case
1175            //    we reflectively set its dkconfig_enabled to false.
1176
1177            // Set the default for not-yet-loaded invariants.
1178            Invariant.invariantEnabledDefault = false;
1179
1180            // Get all loaded classes.  This solution is from
1181            // http://stackoverflow.com/a/10261850/173852 .  Alternate approach:
1182            // http://stackoverflow.com/questions/2548384/java-get-a-list-of-all-classes-loaded-in-the-jvm
1183            Field f;
1184            try {
1185              f = ClassLoader.class.getDeclaredField("classes");
1186            } catch (NoSuchFieldException e) {
1187              throw new Daikon.BugInDaikon("Didn't find field ClassLoader.classes");
1188            }
1189            f.setAccessible(true);
1190            Object classesAsObject;
1191            {
1192              ClassLoader cl = Thread.currentThread().getContextClassLoader();
1193              if (cl == null) {
1194                throw new Daikon.BugInDaikon(
1195                    "Need to handle when getContextClassLoader returns null");
1196              }
1197              try {
1198                classesAsObject = f.get(cl);
1199              } catch (IllegalAccessException e) {
1200                throw new Daikon.BugInDaikon("Field ClassLoader.classes was not made accessible");
1201              }
1202            }
1203            @SuppressWarnings({
1204              "unchecked", // type of ClassLoader.classes field is known
1205              "nullness" // ClassLoader.classes is non-null
1206            })
1207            @NonNull List<Class<?>> classes = (List<Class<?>>) classesAsObject;
1208            for (int i = 0; i < classes.size(); i++) {
1209              Class<?> loadedClass = classes.get(i);
1210              if (Invariant.class.isAssignableFrom(loadedClass)) {
1211                @SuppressWarnings("unchecked") // loadedClass is a subclass of Invariant
1212                Class<? extends Invariant> invType = (Class<? extends Invariant>) loadedClass;
1213                try {
1214                  Field field = invType.getField("dkconfig_enabled");
1215                  if (field.getType() != Boolean.TYPE) {
1216                    throw new Daikon.BugInDaikon(
1217                        "Field "
1218                            + invType
1219                            + ".dkconfig_enabled has type "
1220                            + field.getType()
1221                            + " instead of boolean");
1222                  } else {
1223                    setStaticField(field, false);
1224                    // System.out.println(
1225                    //     "Set field "
1226                    //         + invType
1227                    //         + ".dkconfig_enabled to false");
1228                  }
1229                } catch (NoSuchFieldException e) {
1230                  // System.out.println(
1231                  //     "Class " + invType + " does not have a dkconfig_enabled field");
1232                } catch (IllegalAccessException e) {
1233                  throw new Daikon.BugInDaikon(
1234                      "IllegalAccessException for field " + invType + ".dkconfig_enabled");
1235                }
1236              }
1237            }
1238          } else if (no_dataflow_hierarchy_SWITCH.equals(option_name)) {
1239            use_dataflow_hierarchy = false;
1240          } else if (suppress_redundant_SWITCH.equals(option_name)) {
1241            suppress_redundant_invariants_with_simplify = true;
1242          }
1243
1244          // Process only part of the trace file
1245          else if (ppt_regexp_SWITCH.equals(option_name)) {
1246            if (ppt_regexp != null) {
1247              throw new Daikon.UserError(
1248                  "multiple --"
1249                      + ppt_regexp_SWITCH
1250                      + " regular expressions supplied on command line");
1251            }
1252            String regexp_string = getOptarg(g);
1253            if (!RegexUtil.isRegex(regexp_string)) {
1254              throw new Daikon.UserError(
1255                  "Bad regexp "
1256                      + regexp_string
1257                      + " for "
1258                      + ppt_regexp_SWITCH
1259                      + ": "
1260                      + RegexUtil.regexError(regexp_string));
1261            }
1262            ppt_regexp = Pattern.compile(regexp_string);
1263            break;
1264          } else if (ppt_omit_regexp_SWITCH.equals(option_name)) {
1265            if (ppt_omit_regexp != null) {
1266              throw new Daikon.UserError(
1267                  "multiple --"
1268                      + ppt_omit_regexp_SWITCH
1269                      + " regular expressions supplied on command line");
1270            }
1271            String regexp_string = getOptarg(g);
1272            if (!RegexUtil.isRegex(regexp_string)) {
1273              throw new Daikon.UserError(
1274                  "Bad regexp "
1275                      + regexp_string
1276                      + " for "
1277                      + ppt_omit_regexp_SWITCH
1278                      + ": "
1279                      + RegexUtil.regexError(regexp_string));
1280            }
1281            ppt_omit_regexp = Pattern.compile(regexp_string);
1282            break;
1283          } else if (var_regexp_SWITCH.equals(option_name)) {
1284            if (var_regexp != null) {
1285              throw new Daikon.UserError(
1286                  "multiple --"
1287                      + var_regexp_SWITCH
1288                      + " regular expressions supplied on command line");
1289            }
1290            String regexp_string = getOptarg(g);
1291            if (!RegexUtil.isRegex(regexp_string)) {
1292              throw new Daikon.UserError(
1293                  "Bad regexp "
1294                      + regexp_string
1295                      + " for "
1296                      + var_regexp_SWITCH
1297                      + ": "
1298                      + RegexUtil.regexError(regexp_string));
1299            }
1300            var_regexp = Pattern.compile(regexp_string);
1301            break;
1302          } else if (var_omit_regexp_SWITCH.equals(option_name)) {
1303            if (var_omit_regexp != null) {
1304              throw new Daikon.UserError(
1305                  "multiple --"
1306                      + var_omit_regexp_SWITCH
1307                      + " regular expressions supplied on command line");
1308            }
1309            String regexp_string = getOptarg(g);
1310            if (!RegexUtil.isRegex(regexp_string)) {
1311              throw new Daikon.UserError(
1312                  "Bad regexp "
1313                      + regexp_string
1314                      + " for "
1315                      + var_omit_regexp_SWITCH
1316                      + ": "
1317                      + RegexUtil.regexError(regexp_string));
1318            }
1319            var_omit_regexp = Pattern.compile(regexp_string);
1320            break;
1321          } else if (server_SWITCH.equals(option_name)) {
1322            String input_dir = getOptarg(g);
1323            server_dir = new File(input_dir);
1324            if (!server_dir.isDirectory() || !server_dir.canRead() || !server_dir.canWrite()) {
1325              throw new RuntimeException(
1326                  "Could not open config file in server directory " + server_dir);
1327            }
1328            break;
1329
1330            // Configuration options
1331
1332          } else if (config_SWITCH.equals(option_name)) {
1333            String config_file = getOptarg(g);
1334            try (InputStream stream = new FileInputStream(config_file)) {
1335              Configuration.getInstance().apply(stream);
1336            } catch (IOException e) {
1337              throw new Daikon.UserError(
1338                  // Is this the only possible reason for an IOException?
1339                  "Could not open config file " + config_file);
1340            }
1341            break;
1342          } else if (config_option_SWITCH.equals(option_name)) {
1343            String item = getOptarg(g);
1344            try {
1345              Configuration.getInstance().apply(item);
1346            } catch (daikon.config.Configuration.ConfigException e) {
1347              throw new Daikon.UserError(e);
1348            }
1349            break;
1350          } else if (debugAll_SWITCH.equals(option_name)) {
1351            Global.debugAll = true;
1352          } else if (debug_SWITCH.equals(option_name)) {
1353            LogHelper.setLevel(getOptarg(g), FINE);
1354          } else if (track_SWITCH.equals(option_name)) {
1355            LogHelper.setLevel("daikon.Debug", FINE);
1356            String error = Debug.add_track(getOptarg(g));
1357            if (error != null) {
1358              throw new Daikon.UserError(
1359                  "Error parsing track argument '" + getOptarg(g) + "' - " + error);
1360            }
1361          } else if (disc_reason_SWITCH.equals(option_name)) {
1362            try {
1363              PrintInvariants.discReasonSetup(getOptarg(g));
1364            } catch (IllegalArgumentException e) {
1365              throw new Daikon.UserError(e);
1366            }
1367          } else if (mem_stat_SWITCH.equals(option_name)) {
1368            use_mem_monitor = true;
1369          } else {
1370            throw new Daikon.UserError("Unknown option " + option_name + " on command line");
1371          }
1372          break;
1373        case 'h':
1374          System.out.println(usage);
1375          throw new Daikon.NormalTermination();
1376        case 'o':
1377          String inv_filename = getOptarg(g);
1378
1379          if (inv_file != null) {
1380            throw new Daikon.UserError(
1381                "multiple serialization output files supplied on command line: "
1382                    + inv_file
1383                    + " "
1384                    + inv_filename);
1385          }
1386
1387          inv_file = new File(inv_filename);
1388
1389          if (!FilesPlume.canCreateAndWrite(inv_file)) {
1390            throw new Daikon.UserError("Cannot write to serialization output file " + inv_file);
1391          }
1392          break;
1393        //
1394        case '?':
1395          // break; // getopt() already printed an error
1396          System.out.println(usage);
1397          throw new Daikon.NormalTermination();
1398        //
1399        default:
1400          throw new Daikon.BugInDaikon("getopt() returned " + c);
1401      }
1402    }
1403
1404    // This code is duplicated above within the switch processing.
1405    // First check that all the file names are OK, so we don't do lots of
1406    // processing only to bail out at the end.
1407    for (int i = g.getOptind(); i < args.length; i++) {
1408      String filename = args[i];
1409      if (filename.equals("-") || filename.equals("+")) {
1410        dtrace_files.add(filename);
1411        continue;
1412      }
1413
1414      File file = new File(filename);
1415      if (!file.exists()) {
1416        throw new Daikon.UserError("File " + file + " not found.");
1417      }
1418      filename = file.toString();
1419
1420      // These aren't "endsWith()" because there might be a suffix on the end
1421      // (eg, a date or ".gz").
1422      if (filename.indexOf(".decls") != -1) {
1423        decl_files.add(file);
1424      } else if (filename.indexOf(".dtrace") != -1) {
1425        dtrace_files.add(filename);
1426        // Always output an invariant file by default, even if none is
1427        // specified on the command line.
1428        if (inv_file == null) {
1429          String basename;
1430          // This puts the .inv file in the current directory.
1431          basename = new File(filename).getName();
1432          // This puts the .inv file in the same directory as the .dtrace file.
1433          // basename = filename;
1434          int base_end = basename.indexOf(".dtrace");
1435          String inv_filename = basename.substring(0, base_end) + ".inv.gz";
1436
1437          inv_file = new File(inv_filename);
1438          if (!FilesPlume.canCreateAndWrite(inv_file)) {
1439            throw new Daikon.UserError("Cannot write to file " + inv_file);
1440          }
1441        }
1442      } else if (filename.indexOf(".spinfo") != -1) {
1443        spinfo_files.add(file);
1444      } else if (filename.indexOf(".map") != -1) {
1445        map_files.add(file);
1446      } else {
1447        throw new Daikon.UserError("Unrecognized file type: " + file);
1448      }
1449    }
1450
1451    // Set the fuzzy float comparison ratio.  This needs to be done after
1452    // any configuration options (which may set the ratio) are processed.
1453    Global.fuzzy.setRelativeRatio(Invariant.dkconfig_fuzzy_ratio);
1454
1455    // Setup ppt_max_name based on the specified percentage of ppts to process
1456    if (dkconfig_ppt_perc != 100) {
1457      ppt_max_name = setup_ppt_perc(decl_files, dkconfig_ppt_perc);
1458      System.out.println("Max ppt name = " + ppt_max_name);
1459    }
1460
1461    // Validate guardNulls option
1462    PrintInvariants.validateGuardNulls();
1463
1464    return new FileOptions(decl_files, dtrace_files, spinfo_files, map_files);
1465  }
1466
1467  /**
1468   * Set a static field to the given value.
1469   *
1470   * @param field a field; must be static
1471   * @param value the value to set the field to
1472   * @throws IllegalAccessException if {@code field} is enforcing Java language access control and
1473   *     the underlying field is either inaccessible or final
1474   */
1475  // This method exists to reduce the scope of the warning suppression.
1476  @SuppressWarnings({
1477    "nullness:argument", // field is static, so object may be null
1478    "interning:argument" // interning is not necessary for how this method is used
1479  })
1480  private static void setStaticField(Field field, Object value) throws IllegalAccessException {
1481    field.set(null, value);
1482  }
1483
1484  /**
1485   * Just like {@code g.getOptarg()}, but only to be called in circumstances when the programmer
1486   * knows that the return value is non-null.
1487   *
1488   * @param g a command-line argument processor
1489   * @return the value for an argument found by {@code g}
1490   */
1491  @Pure
1492  public static String getOptarg(Getopt g) {
1493    String result = g.getOptarg();
1494    if (result == null) {
1495      throw new Error("getOptarg returned null for " + g);
1496    }
1497    return result;
1498  }
1499
1500  /**
1501   * Invariants passed on the command line with the {@code --user_defined_invariant} option. A list
1502   * of class names in the format required by {@link Class#forName(String)}.
1503   */
1504  private static List<@ClassGetName String> userDefinedInvariants =
1505      new ArrayList<@ClassGetName String>();
1506
1507  /**
1508   * Creates the list of prototype invariants for all Daikon invariants. New invariants must be
1509   * added to this list.
1510   */
1511  public static void setup_proto_invs() {
1512
1513    // Unary scalar invariants
1514    {
1515      // OneOf (OneOf.java.jpp)
1516      proto_invs.add(OneOfScalar.get_proto());
1517      proto_invs.add(OneOfFloat.get_proto());
1518      proto_invs.add(OneOfString.get_proto());
1519
1520      // NonZero (NonZero.java.jpp)
1521      proto_invs.add(NonZero.get_proto());
1522      proto_invs.add(NonZeroFloat.get_proto());
1523
1524      proto_invs.add(IsPointer.get_proto());
1525
1526      // Lower and Upper bound (Bound.java.jpp)
1527      proto_invs.add(LowerBound.get_proto());
1528      proto_invs.add(LowerBoundFloat.get_proto());
1529      proto_invs.add(UpperBound.get_proto());
1530      proto_invs.add(UpperBoundFloat.get_proto());
1531
1532      // Modulus and NonModulus (Modulus.java and NonModulus.java)
1533      proto_invs.add(Modulus.get_proto());
1534      proto_invs.add(NonModulus.get_proto());
1535
1536      // Range invariant (Range.java.jpp)
1537      proto_invs.addAll(RangeInt.get_proto_all());
1538      proto_invs.addAll(RangeFloat.get_proto_all());
1539
1540      // Printable String
1541      proto_invs.add(PrintableString.get_proto());
1542
1543      // Complete One Of
1544      proto_invs.add(CompleteOneOfString.get_proto());
1545      proto_invs.add(CompleteOneOfScalar.get_proto());
1546
1547      // Positive (x > 0) (Postive.java).  Positive is a sample invariant
1548      // that is only included as an example.
1549      // proto_invs.add (Postive.get_proto());
1550    }
1551
1552    // Unary sequence invariants
1553    {
1554      // OneOf (OneOf.java.jpp)
1555      proto_invs.add(OneOfSequence.get_proto());
1556      proto_invs.add(OneOfFloatSequence.get_proto());
1557      proto_invs.add(OneOfStringSequence.get_proto());
1558      proto_invs.add(EltOneOf.get_proto());
1559      proto_invs.add(EltOneOfFloat.get_proto());
1560      proto_invs.add(EltOneOfString.get_proto());
1561
1562      // Range invariant (Range.java.jpp)
1563      proto_invs.addAll(EltRangeInt.get_proto_all());
1564      proto_invs.addAll(EltRangeFloat.get_proto_all());
1565
1566      // Sequence Index Comparisons (SeqIndexComparison.java.jpp)
1567      proto_invs.add(SeqIndexIntEqual.get_proto());
1568      proto_invs.add(SeqIndexIntNonEqual.get_proto());
1569      proto_invs.add(SeqIndexIntGreaterEqual.get_proto());
1570      proto_invs.add(SeqIndexIntGreaterThan.get_proto());
1571      proto_invs.add(SeqIndexIntLessEqual.get_proto());
1572      proto_invs.add(SeqIndexIntLessThan.get_proto());
1573      proto_invs.add(SeqIndexFloatEqual.get_proto());
1574      proto_invs.add(SeqIndexFloatNonEqual.get_proto());
1575      proto_invs.add(SeqIndexFloatGreaterEqual.get_proto());
1576      proto_invs.add(SeqIndexFloatGreaterThan.get_proto());
1577      proto_invs.add(SeqIndexFloatLessEqual.get_proto());
1578      proto_invs.add(SeqIndexFloatLessThan.get_proto());
1579
1580      // foreach i compare a[i] to a[i+1] (EltwiseIntComparisons.java.jpp)
1581      proto_invs.add(EltwiseIntEqual.get_proto());
1582      proto_invs.add(EltwiseIntLessEqual.get_proto());
1583      proto_invs.add(EltwiseIntGreaterEqual.get_proto());
1584      proto_invs.add(EltwiseIntLessThan.get_proto());
1585      proto_invs.add(EltwiseIntGreaterThan.get_proto());
1586      proto_invs.add(EltwiseFloatEqual.get_proto());
1587      proto_invs.add(EltwiseFloatLessEqual.get_proto());
1588      proto_invs.add(EltwiseFloatGreaterEqual.get_proto());
1589      proto_invs.add(EltwiseFloatLessThan.get_proto());
1590      proto_invs.add(EltwiseFloatGreaterThan.get_proto());
1591
1592      // EltNonZero (EltNonZero.java.jpp)
1593      proto_invs.add(EltNonZero.get_proto());
1594      proto_invs.add(EltNonZeroFloat.get_proto());
1595
1596      // No Duplicates (NoDuplicates.java.jpp)
1597      proto_invs.add(NoDuplicates.get_proto());
1598      proto_invs.add(NoDuplicatesFloat.get_proto());
1599
1600      // Element bounds (Bound.java.jpp)
1601      proto_invs.add(EltLowerBound.get_proto());
1602      proto_invs.add(EltUpperBound.get_proto());
1603      proto_invs.add(EltLowerBoundFloat.get_proto());
1604      proto_invs.add(EltUpperBoundFloat.get_proto());
1605
1606      // CommonSequence (CommonSequence.java.jpp)
1607      proto_invs.add(CommonSequence.get_proto());
1608      proto_invs.add(CommonFloatSequence.get_proto());
1609
1610      // CommonStringSequence (CommonStringSubsequence.java)
1611      proto_invs.add(CommonStringSequence.get_proto());
1612    }
1613
1614    // Binary scalar-scalar invariants
1615    {
1616      // Int, Float, String comparisons (from IntComparisons.java.jpp)
1617      proto_invs.add(IntEqual.get_proto());
1618      proto_invs.add(IntNonEqual.get_proto());
1619      proto_invs.add(IntLessThan.get_proto());
1620      proto_invs.add(IntGreaterThan.get_proto());
1621      proto_invs.add(IntLessEqual.get_proto());
1622      proto_invs.add(IntGreaterEqual.get_proto());
1623      proto_invs.add(FloatEqual.get_proto());
1624      proto_invs.add(FloatNonEqual.get_proto());
1625      proto_invs.add(FloatLessThan.get_proto());
1626      proto_invs.add(FloatGreaterThan.get_proto());
1627      proto_invs.add(FloatLessEqual.get_proto());
1628      proto_invs.add(FloatGreaterEqual.get_proto());
1629      proto_invs.add(StringEqual.get_proto());
1630      proto_invs.add(StringNonEqual.get_proto());
1631      proto_invs.add(StringLessThan.get_proto());
1632      proto_invs.add(StringGreaterThan.get_proto());
1633      proto_invs.add(StringLessEqual.get_proto());
1634      proto_invs.add(StringGreaterEqual.get_proto());
1635
1636      // LinearBinary over integer/float (from LinearBinary.java.jpp)
1637      proto_invs.add(LinearBinary.get_proto());
1638      proto_invs.add(LinearBinaryFloat.get_proto());
1639
1640      // Numeric invariants (from Numeric.java.jpp)
1641      proto_invs.addAll(NumericInt.get_proto_all());
1642      proto_invs.addAll(NumericFloat.get_proto_all());
1643
1644      // Standard binary string invariants
1645      proto_invs.addAll(StdString.get_proto_all());
1646    }
1647
1648    // Binary sequence-sequence invariants
1649    {
1650      // Numeric invariants (from Numeric.java.jpp)
1651      proto_invs.addAll(PairwiseNumericInt.get_proto_all());
1652      proto_invs.addAll(PairwiseNumericFloat.get_proto_all());
1653
1654      // Pairwise string invariants (also from Numeric.java.jpp)
1655      proto_invs.addAll(PairwiseString.get_proto_all());
1656
1657      // Lexical sequence comparisons (from SeqComparison.java.jpp)
1658      proto_invs.add(SeqSeqIntEqual.get_proto());
1659      proto_invs.add(SeqSeqIntLessThan.get_proto());
1660      proto_invs.add(SeqSeqIntGreaterThan.get_proto());
1661      proto_invs.add(SeqSeqIntLessEqual.get_proto());
1662      proto_invs.add(SeqSeqIntGreaterEqual.get_proto());
1663      proto_invs.add(SeqSeqFloatEqual.get_proto());
1664      proto_invs.add(SeqSeqFloatLessThan.get_proto());
1665      proto_invs.add(SeqSeqFloatGreaterThan.get_proto());
1666      proto_invs.add(SeqSeqFloatLessEqual.get_proto());
1667      proto_invs.add(SeqSeqFloatGreaterEqual.get_proto());
1668      proto_invs.add(SeqSeqStringEqual.get_proto());
1669      proto_invs.add(SeqSeqStringLessThan.get_proto());
1670      proto_invs.add(SeqSeqStringGreaterThan.get_proto());
1671      proto_invs.add(SeqSeqStringLessEqual.get_proto());
1672      proto_invs.add(SeqSeqStringGreaterEqual.get_proto());
1673
1674      // Pairwise sequence comparisons (from PairwiseIntComparison.java.jpp)
1675      proto_invs.add(PairwiseIntEqual.get_proto());
1676      proto_invs.add(PairwiseIntLessThan.get_proto());
1677      proto_invs.add(PairwiseIntGreaterThan.get_proto());
1678      proto_invs.add(PairwiseIntLessEqual.get_proto());
1679      proto_invs.add(PairwiseIntGreaterEqual.get_proto());
1680      proto_invs.add(PairwiseFloatEqual.get_proto());
1681      proto_invs.add(PairwiseFloatLessThan.get_proto());
1682      proto_invs.add(PairwiseFloatGreaterThan.get_proto());
1683      proto_invs.add(PairwiseFloatLessEqual.get_proto());
1684      proto_invs.add(PairwiseFloatGreaterEqual.get_proto());
1685      proto_invs.add(PairwiseStringEqual.get_proto());
1686      proto_invs.add(PairwiseStringLessThan.get_proto());
1687      proto_invs.add(PairwiseStringGreaterThan.get_proto());
1688      proto_invs.add(PairwiseStringLessEqual.get_proto());
1689      proto_invs.add(PairwiseStringGreaterEqual.get_proto());
1690
1691      // Array Reverse (from Reverse.java.jpp)
1692      proto_invs.add(Reverse.get_proto());
1693      proto_invs.add(ReverseFloat.get_proto());
1694
1695      // Pairwise Linear Binary (from PairwiseLinearBinary.java.jpp)
1696      proto_invs.add(PairwiseLinearBinary.get_proto());
1697      proto_invs.add(PairwiseLinearBinaryFloat.get_proto());
1698
1699      // Subset and Superset (from SubSet.java.jpp)
1700      proto_invs.add(SubSet.get_proto());
1701      proto_invs.add(SuperSet.get_proto());
1702      proto_invs.add(SubSetFloat.get_proto());
1703      proto_invs.add(SuperSetFloat.get_proto());
1704
1705      // Subsequence (from SubSequence.java.jpp)
1706      proto_invs.add(SubSequence.get_proto());
1707      proto_invs.add(SubSequenceFloat.get_proto());
1708      proto_invs.add(SuperSequence.get_proto());
1709      proto_invs.add(SuperSequenceFloat.get_proto());
1710    }
1711
1712    // Binary sequence-scalar invariants
1713    {
1714      // Comparison of scalar to each array element (SeqIntComparison.java.jpp)
1715      proto_invs.add(SeqIntEqual.get_proto());
1716      proto_invs.add(SeqIntLessThan.get_proto());
1717      proto_invs.add(SeqIntGreaterThan.get_proto());
1718      proto_invs.add(SeqIntLessEqual.get_proto());
1719      proto_invs.add(SeqIntGreaterEqual.get_proto());
1720      proto_invs.add(SeqFloatEqual.get_proto());
1721      proto_invs.add(SeqFloatLessThan.get_proto());
1722      proto_invs.add(SeqFloatGreaterThan.get_proto());
1723      proto_invs.add(SeqFloatLessEqual.get_proto());
1724      proto_invs.add(SeqFloatGreaterEqual.get_proto());
1725
1726      // Scalar is an element of the array (Member.java.jpp)
1727      proto_invs.add(Member.get_proto());
1728      proto_invs.add(MemberFloat.get_proto());
1729      proto_invs.add(MemberString.get_proto());
1730    }
1731
1732    // Ternary invariants
1733    {
1734      // FunctionBinary (FunctionBinary.java.jpp)
1735      proto_invs.addAll(FunctionBinary.get_proto_all());
1736      proto_invs.addAll(FunctionBinaryFloat.get_proto_all());
1737
1738      // LinearTernary (LinearTernary.java.jpp)
1739      proto_invs.add(LinearTernary.get_proto());
1740      proto_invs.add(LinearTernaryFloat.get_proto());
1741    }
1742
1743    // User-defined invariants
1744    for (String invariantClassName : userDefinedInvariants) {
1745      Class<?> invClass;
1746      try {
1747        invClass = Class.forName(invariantClassName);
1748      } catch (ClassNotFoundException e) {
1749        throw new Daikon.UserError(
1750            "Cannot load class "
1751                + invariantClassName
1752                + " in "
1753                + user_defined_invariant_SWITCH
1754                + " command-line argument; is it on the classpath?");
1755      }
1756      Method get_proto_method;
1757      try {
1758        get_proto_method = invClass.getMethod("get_proto");
1759      } catch (NoSuchMethodException e) {
1760        throw new Daikon.UserError(
1761            "No get_proto() method in user-defined invariant class " + invariantClassName);
1762      } catch (SecurityException e) {
1763        throw new Daikon.UserError(
1764            e,
1765            "SecurityException while looking up get_proto() method in user-defined invariant class "
1766                + invariantClassName);
1767      }
1768      Invariant inv;
1769      try {
1770        @SuppressWarnings("nullness") // null argument is OK because get_proto_method is static
1771        Object inv_as_object = get_proto_method.invoke(null);
1772        if (inv_as_object == null) {
1773          throw new Daikon.UserError(
1774              invariantClassName
1775                  + ".get_proto() returned null but should have returned an Invariant");
1776        }
1777        if (!(inv_as_object instanceof Invariant)) {
1778          Class<?> cls = inv_as_object.getClass();
1779          throw new Daikon.UserError(
1780              invariantClassName
1781                  + ".get_proto() returned object of the wrong type."
1782                  + "  It should have been a subclass of invariant, but was "
1783                  + cls
1784                  + ": "
1785                  + inv_as_object);
1786        }
1787        inv = (Invariant) inv_as_object;
1788      } catch (Exception e) {
1789        throw new Daikon.UserError(
1790            e, "Exception while invoking " + invariantClassName + ".get_proto()");
1791      }
1792      proto_invs.add(inv);
1793    }
1794
1795    // Remove any elements that are not enabled
1796    for (Iterator<@Prototype Invariant> i = proto_invs.iterator(); i.hasNext(); ) {
1797      @Prototype Invariant inv = i.next();
1798      assert inv != null;
1799      if (!inv.enabled()) {
1800        i.remove();
1801      }
1802    }
1803  }
1804
1805  /**
1806   * Creates invariants for upper program points by merging together the invariants from all of the
1807   * lower points.
1808   */
1809  public static void createUpperPpts(PptMap all_ppts) {
1810
1811    // Process each ppt that doesn't have a parent
1812    // (mergeInvs is called on a root, and recursively processes children)
1813    for (PptTopLevel ppt : all_ppts.pptIterable()) {
1814      // System.out.printf("considering ppt %s parents: %s, children: %s%n",
1815      //                     ppt.name, ppt.parents, ppt.children);
1816      if (ppt.parents.size() == 0) {
1817        ppt.mergeInvs();
1818      }
1819    }
1820  }
1821
1822  /** Setup splitters. Add orig and derived variables. Recursively call init_ppt on splits. */
1823  public static void init_ppt(PptTopLevel ppt, PptMap all_ppts) {
1824
1825    if (!Daikon.using_DaikonSimple) {
1826      // Create orig variables and set up splitters.
1827      // This must be done before adding derived variables.
1828      // Do not add splitters to ppts that were already created by splitters!
1829      // Also, ppts created by splitters already have their orig_vars.
1830      if (!(ppt instanceof PptConditional)) {
1831        progress = "Creating orig variables and splitters for: " + ppt.name;
1832        create_orig_vars(ppt, all_ppts);
1833        setup_splitters(ppt);
1834      }
1835    }
1836
1837    // Create derived variables
1838    if (!Derivation.dkconfig_disable_derived_variables) {
1839      progress = "Creating derived variables for: " + ppt.name;
1840      ppt.create_derived_variables();
1841    }
1842
1843    if (!Daikon.using_DaikonSimple) {
1844      // Initialize equality sets on leaf nodes
1845      setupEquality(ppt);
1846      // System.out.printf("initialized equality %s for ppt %s%n",
1847      //                    ppt.equality_view, ppt.name());
1848
1849      // Recursively initialize ppts created by splitters
1850      if (ppt.has_splitters()) {
1851        for (PptConditional ppt_cond : ppt.cond_iterable()) {
1852          init_ppt(ppt_cond, all_ppts);
1853        }
1854      }
1855    }
1856
1857    if (VarInfo.assertionsEnabled()) {
1858      for (VarInfo vi : ppt.var_infos) {
1859        vi.checkRep();
1860      }
1861    }
1862  }
1863
1864  /** Create EXIT program points as needed for EXITnn program points. */
1865  public static void create_combined_exits(PptMap ppts) {
1866
1867    // We can't add the newly created exit Ppts directly to ppts while we
1868    // are iterating over it, so store them temporarily in this map.
1869    PptMap exit_ppts = new PptMap();
1870
1871    for (PptTopLevel ppt : ppts.pptIterable()) {
1872      // skip unless it's an EXITnn
1873      if (!ppt.is_subexit()) {
1874        continue;
1875      }
1876
1877      PptTopLevel exitnn_ppt = ppt;
1878      PptName exit_name = ppt.ppt_name.makeExit();
1879      PptTopLevel exit_ppt = exit_ppts.get(exit_name);
1880
1881      if (debugInit.isLoggable(FINE)) {
1882        debugInit.fine("create_combined_exits: encountered exit " + exitnn_ppt.name());
1883      }
1884
1885      // Create the exit, if necessary
1886      if (exit_ppt == null) {
1887        // This is a hack.  It should probably filter out orig and derived
1888        // vars instead of taking the first n.
1889        int len = ppt.num_tracevars + ppt.num_static_constant_vars;
1890        VarInfo[] exit_vars = new VarInfo[len];
1891        // System.out.printf("new decl fmt = %b%n", FileIO.new_decl_format);
1892        for (int j = 0; j < len; j++) {
1893          @SuppressWarnings("interning") // about to be used in new program point
1894          @Interned VarInfo exit_var = new VarInfo(ppt.var_infos[j]);
1895          exit_vars[j] = exit_var;
1896          // System.out.printf("exitNN name '%s', exit name '%s'%n",
1897          //                   ppt.var_infos[j].name(), exit_vars[j].name());
1898          exit_vars[j].varinfo_index = ppt.var_infos[j].varinfo_index;
1899          exit_vars[j].value_index = ppt.var_infos[j].value_index;
1900          // Don't inherit the entry variable's equalitySet.
1901          @SuppressWarnings("nullness") // reinitialization
1902          @NonNull Equality es = null;
1903          exit_vars[j].equalitySet = es;
1904        }
1905
1906        exit_ppt =
1907            new PptTopLevel(
1908                exit_name.getName(),
1909                PptTopLevel.PptType.EXIT,
1910                ppt.parent_relations,
1911                ppt.flags,
1912                exit_vars);
1913
1914        // exit_ppt.ppt_name.setVisibility(exitnn_name.getVisibility());
1915        exit_ppts.add(exit_ppt);
1916        if (debugInit.isLoggable(FINE)) {
1917          debugInit.fine("create_combined_exits: created exit " + exit_name);
1918        }
1919        init_ppt(exit_ppt, ppts);
1920      }
1921    }
1922
1923    // Now add the newly created Ppts to the global map.
1924    for (PptTopLevel ppt : exit_ppts.pptIterable()) {
1925      ppts.add(ppt);
1926    }
1927  }
1928
1929  // The function filters out the reflexive invs in binary slices,
1930  // reflexive and partially reflexive invs in ternary slices
1931  // and also filters out the invariants that have not seen enough
1932  // samples in ternary slices.
1933  static List<Invariant> filter_invs(List<Invariant> invs) {
1934    List<Invariant> new_list = new ArrayList<>();
1935
1936    for (Invariant inv : invs) {
1937      VarInfo[] vars = inv.ppt.var_infos;
1938
1939      // This check is the most non-intrusive way to filter out the invs
1940      // Filter out reflexive invariants in the binary invs
1941      if (!((inv.ppt instanceof PptSlice2) && vars[0] == vars[1])) {
1942
1943        // Filter out the reflexive and partially reflexive invs in the
1944        // ternary slices
1945        if (!((inv.ppt instanceof PptSlice3)
1946            && (vars[0] == vars[1] || vars[1] == vars[2] || vars[0] == vars[2]))) {
1947          if (inv.ppt.num_values() != 0) {
1948
1949            // filters out "warning: too few samples for
1950            // daikon.inv.ternary.threeScalar.LinearTernary invariant"
1951            if (inv.isActive()) {
1952              new_list.add(inv);
1953            }
1954          }
1955        }
1956      }
1957    }
1958
1959    return new_list;
1960  }
1961
1962  /**
1963   * Add orig() variables to the given EXIT/EXITnn point. Does nothing if exit_ppt is not an
1964   * EXIT/EXITnn.
1965   */
1966  private static void create_orig_vars(PptTopLevel exit_ppt, PptMap ppts) {
1967    if (!exit_ppt.ppt_name.isExitPoint()) {
1968      if (VarInfo.assertionsEnabled()) {
1969        for (VarInfo vi : exit_ppt.var_infos) {
1970          try {
1971            vi.checkRep();
1972          } catch (Throwable e) {
1973            System.err.println();
1974            System.err.println("Error with variable " + vi + " at ppt " + exit_ppt);
1975            throw e;
1976          }
1977        }
1978      }
1979      return;
1980    }
1981
1982    if (debugInit.isLoggable(FINE)) {
1983      debugInit.fine("Doing create and relate orig vars for: " + exit_ppt.name());
1984    }
1985
1986    PptTopLevel entry_ppt = ppts.get(exit_ppt.ppt_name.makeEnter());
1987    if (entry_ppt == null) {
1988      throw new Daikon.UserError("exit found with no corresponding entry: " + exit_ppt.name());
1989    }
1990
1991    // Add "orig(...)" (prestate) variables to the program point.
1992    // Don't bother to include the constants.  Walk through
1993    // entry_ppt's vars.  For each non-constant, put it on the
1994    // new_vis worklist after fixing its comparability information.
1995    exit_ppt.num_orig_vars = entry_ppt.num_tracevars;
1996    VarInfo[] new_vis = new VarInfo[exit_ppt.num_orig_vars];
1997    {
1998      VarInfo[] entry_ppt_vis = entry_ppt.var_infos;
1999      int new_vis_index = 0;
2000      for (int k = 0; k < entry_ppt.num_declvars; k++) {
2001        VarInfo vi = entry_ppt_vis[k];
2002        assert !vi.isDerived() : "Derived when making orig(): " + vi.name();
2003        if (vi.isStaticConstant()) {
2004          continue;
2005        }
2006        VarInfo origvar = VarInfo.origVarInfo(vi);
2007        // Fix comparability
2008        VarInfo postvar = exit_ppt.find_var_by_name(vi.name());
2009        if (postvar == null) {
2010          System.out.printf(
2011              "Looking for postvar, can't find var %s in exit of ppt %s%n", vi, exit_ppt.name());
2012          for (VarInfo cvi : entry_ppt.var_infos) {
2013            System.out.printf("  entry var = %s%n", cvi);
2014          }
2015          for (VarInfo cvi : exit_ppt.var_infos) {
2016            System.out.printf("  exit var = %s%n", cvi);
2017          }
2018          throw new RuntimeException("this can't happen: postvar is null");
2019        }
2020        origvar.postState = postvar;
2021        origvar.comparability = postvar.comparability.makeAlias();
2022
2023        // add parents for override relations
2024        // exit_ppt.parents has not been loaded at this point
2025        for (VarParent pi : postvar.parents) {
2026          PptTopLevel parentppt = ppts.get(pi.parent_ppt);
2027          if (parentppt != null) {
2028            if (!parentppt.is_object() && !parentppt.is_class()) {
2029              VarInfo pvi =
2030                  parentppt.find_var_by_name(
2031                      pi.parent_variable == null ? postvar.name() : pi.parent_variable);
2032              if (pvi != null) {
2033                origvar.parents.add(
2034                    new VarParent(pi.parent_ppt, pi.parent_relation_id, pvi.prestate_name()));
2035              }
2036            }
2037          }
2038        }
2039
2040        // Add to new_vis
2041        new_vis[new_vis_index] = origvar;
2042        new_vis_index++;
2043        // System.out.printf("adding origvar %s to ppt %s%n", origvar.name(),
2044        //                   exit_ppt.name());
2045      }
2046      assert new_vis_index == exit_ppt.num_orig_vars;
2047    }
2048    exit_ppt.addVarInfos(new_vis);
2049
2050    if (VarInfo.assertionsEnabled()) {
2051      for (VarInfo vi : exit_ppt.var_infos) {
2052        try {
2053          vi.checkRep();
2054        } catch (Throwable e) {
2055          System.err.println();
2056          System.err.println("Error with variable " + vi + " at ppt " + exit_ppt);
2057          throw e;
2058        }
2059      }
2060    }
2061  }
2062
2063  // ///////////////////////////////////////////////////////////////////////////
2064  // Read decls, dtrace, etc. files
2065
2066  /**
2067   * Load a set of decl files.
2068   *
2069   * @param decl_files the files to load
2070   * @return the PptMap for the loaded files
2071   */
2072  @RequiresNonNull("fileio_progress")
2073  // set in mainHelper
2074  private static PptMap load_decls_files(Set<File> decl_files) {
2075    long startTime = System.nanoTime();
2076    try {
2077      if (!Daikon.dkconfig_quiet) {
2078        System.out.print("Reading declaration files ");
2079      }
2080      PptMap all_ppts = FileIO.read_declaration_files(decl_files);
2081      if (debugTrace.isLoggable(FINE)) {
2082        debugTrace.fine("Initializing partial order");
2083      }
2084      fileio_progress.clear();
2085      if (!Daikon.dkconfig_quiet && decl_files.size() > 0) {
2086        System.out.print("\r(read ");
2087        System.out.print(StringsPlume.nplural(decl_files.size(), "decls file"));
2088        System.out.println(")");
2089      }
2090      return all_ppts;
2091    } catch (IOException e) {
2092      // System.out.println();
2093      // e.printStackTrace();
2094      throw new Daikon.UserError(e, "Error parsing decl file");
2095    } finally {
2096      long duration = System.nanoTime() - startTime;
2097      debugProgress.fine(
2098          "Time spent on read_declaration_files: " + TimeUnit.NANOSECONDS.toSeconds(duration));
2099    }
2100  }
2101
2102  private static void load_spinfo_files(Set<File> spinfo_files) {
2103    if (PptSplitter.dkconfig_disable_splitting || spinfo_files.isEmpty()) {
2104      return;
2105    }
2106    long startTime = System.nanoTime();
2107    try {
2108      System.out.print("Reading splitter info files ");
2109      create_splitters(spinfo_files);
2110      System.out.print("\r(read ");
2111      System.out.print(StringsPlume.nplural(spinfo_files.size(), "spinfo file"));
2112      System.out.print(", ");
2113      System.out.print(
2114          StringsPlume.nplural(SpinfoFile.numSplittterObjects(spinfoFiles), "splitter"));
2115      System.out.println(")");
2116    } catch (IOException e) {
2117      System.out.println();
2118      e.printStackTrace();
2119      throw new Error(e);
2120    } finally {
2121      long duration = System.nanoTime() - startTime;
2122      debugProgress.fine(
2123          "Time spent on load_spinfo_files: " + TimeUnit.NANOSECONDS.toSeconds(duration));
2124    }
2125  }
2126
2127  /**
2128   * A wrapper around {@link ContextSplitterFactory#load_mapfiles_into_splitterlist}.
2129   *
2130   * @param map_files the map files to read into SplitterList
2131   */
2132  private static void load_map_files(Set<File> map_files) {
2133    long startTime = System.nanoTime();
2134    if (!PptSplitter.dkconfig_disable_splitting && map_files.size() > 0) {
2135      System.out.print("Reading map (context) files ");
2136      ContextSplitterFactory.load_mapfiles_into_splitterlist(
2137          map_files, ContextSplitterFactory.dkconfig_granularity);
2138      System.out.print("\r(read ");
2139      System.out.print(StringsPlume.nplural(map_files.size(), "map (context) file"));
2140      System.out.println(")");
2141      long duration = System.nanoTime() - startTime;
2142      debugProgress.fine(
2143          "Time spent on load_map_files: " + TimeUnit.NANOSECONDS.toSeconds(duration));
2144    }
2145  }
2146
2147  /**
2148   * Sets up splitting on all ppts. Currently only binary splitters over boolean returns or exactly
2149   * two return statements are enabled by default (though other splitters can be defined by the
2150   * user).
2151   *
2152   * @param ppt the program point to add conditions to
2153   */
2154  @SuppressWarnings("nullness:contracts.precondition")
2155  public static void setup_splitters(PptTopLevel ppt) {
2156    if (PptSplitter.dkconfig_disable_splitting) {
2157      return;
2158    }
2159
2160    Global.debugSplit.fine("<<enter>> setup_splitters");
2161
2162    SplitterFactory.load_splitters(ppt, spinfoFiles);
2163
2164    Splitter[] pconds;
2165    if (SplitterList.dkconfig_all_splitters) {
2166      pconds = SplitterList.get_all();
2167    } else {
2168      pconds = SplitterList.get(ppt.name());
2169    }
2170    if (pconds != null) {
2171      Global.debugSplit.fine(
2172          "Got " + StringsPlume.nplural(pconds.length, "splitter") + " for " + ppt.name());
2173      ppt.addConditions(pconds);
2174    }
2175
2176    Global.debugSplit.fine("<<exit>>  setup_splitters");
2177  }
2178
2179  // ///////////////////////////////////////////////////////////////////////////
2180  // Infer invariants over the trace data
2181
2182  /**
2183   * The number of columns of progress information to display. In many Unix shells, this can be set
2184   * to an appropriate value by {@code --config_option
2185   * daikon.Daikon.progress_display_width=$COLUMNS}.
2186   */
2187  public static int dkconfig_progress_display_width = 80;
2188
2189  /**
2190   * Human-friendly progress status message. If {@code fileio_progress} is non-null, then this is
2191   * ignored. So this is primarily for progress reports that are not IO-related.
2192   */
2193  public static String progress = "";
2194
2195  // Is set unconditionally in mainHelper
2196  /** Takes precedence over the progress variable. */
2197  private static @MonotonicNonNull FileIOProgress fileio_progress = null;
2198
2199  /** Outputs FileIO progress information. Uses global variable FileIO.data_trace_state. */
2200  public static class FileIOProgress extends Thread {
2201    public FileIOProgress() {
2202      setDaemon(true);
2203    }
2204
2205    /**
2206     * Clients should set this variable instead of calling Thread.stop(), which is deprecated.
2207     * Typically a client calls "display()" before setting this. The stopping happens later, and
2208     * calls clear() anyway.
2209     */
2210    public boolean shouldStop = false;
2211
2212    @Override
2213    public void run() {
2214      if (dkconfig_progress_delay == -1) {
2215        return;
2216      }
2217      while (true) {
2218        if (shouldStop) {
2219          clear();
2220          return;
2221        }
2222        display();
2223        try {
2224          sleep(dkconfig_progress_delay);
2225        } catch (InterruptedException e) {
2226          // hmm
2227        }
2228      }
2229    }
2230
2231    /** Clear the display; good to do before printing to System.out. */
2232    public void clear() {
2233      if (dkconfig_progress_delay == -1) {
2234        return;
2235      }
2236      // "display("");" is wrong becuase it leaves the timestamp and writes
2237      // spaces across the screen.
2238      String status = StringsPlume.rpad("", dkconfig_progress_display_width - 1);
2239      System.out.print("\r" + status);
2240      System.out.print("\r"); // return to beginning of line
2241      System.out.flush();
2242    }
2243
2244    /**
2245     * Displays the current status. Call this if you don't want to wait until the next automatic
2246     * display.
2247     */
2248    public void display() {
2249      if (dkconfig_progress_delay == -1) {
2250        return;
2251      }
2252
2253      String message;
2254      if (FileIO.data_trace_state != null) {
2255        message = FileIO.data_trace_state.reading_message();
2256      } else {
2257        if (Daikon.progress == null) {
2258          message = "[no status]";
2259        } else {
2260          message = Daikon.progress;
2261        }
2262      }
2263      display(message);
2264    }
2265
2266    /**
2267     * Displays the given message.
2268     *
2269     * @param message message to be displayed
2270     */
2271    public void display(String message) {
2272      if (dkconfig_progress_delay == -1) {
2273        return;
2274      }
2275      String status =
2276          StringsPlume.rpad(
2277              "[" + LocalDateTime.now(ZoneId.systemDefault()) + "]: " + message,
2278              dkconfig_progress_display_width - 1);
2279      System.out.print("\r" + status);
2280      System.out.flush();
2281      // System.out.println (status); // for debugging
2282
2283      if (debugTrace.isLoggable(FINE)) {
2284        debugTrace.fine("Free memory: " + java.lang.Runtime.getRuntime().freeMemory());
2285        debugTrace.fine(
2286            "Used memory: "
2287                + (java.lang.Runtime.getRuntime().totalMemory()
2288                    - java.lang.Runtime.getRuntime().freeMemory()));
2289        try {
2290          if (FileIO.data_trace_state != null) {
2291            debugTrace.fine("Active slices: " + FileIO.data_trace_state.all_ppts.countSlices());
2292          }
2293        } catch (ConcurrentModificationException e) {
2294          // Because this code is a separate thread, the number of ppts
2295          // could change during countSlices.  Just ignore and continue.
2296        }
2297      }
2298    }
2299  }
2300
2301  /**
2302   * The data-processing routine of the daikon engine. At this point, the decls and spinfo files
2303   * have been loaded, all of the program points have been setup, and candidate invariants have been
2304   * instantiated. This routine processes data to falsify the candidate invariants.
2305   */
2306  @RequiresNonNull("fileio_progress")
2307  // set in mainHelper
2308  private static void process_data(PptMap all_ppts, Set<String> dtrace_files) {
2309    MemMonitor monitor = null;
2310    if (use_mem_monitor) {
2311      monitor = new MemMonitor("stat.out");
2312      new Thread((Runnable) monitor).start();
2313    }
2314
2315    long startTime = System.nanoTime();
2316
2317    // Preprocessing
2318    setup_NISuppression();
2319
2320    // Processing (actually using dtrace files)
2321    try {
2322      fileio_progress.clear();
2323      if (!Daikon.dkconfig_quiet) {
2324        System.out.println(
2325            "Processing trace data; reading "
2326                + StringsPlume.nplural(dtrace_files.size(), "dtrace file")
2327                + ":");
2328      }
2329      FileIO.read_data_trace_files(dtrace_files, all_ppts);
2330      // Final update, so "100%", not "99.70%", is the last thing printed.
2331      // (This doesn't seem to achieve that, though...)
2332      fileio_progress.display();
2333      fileio_progress.shouldStop = true;
2334      fileio_progress.display();
2335      if (!Daikon.dkconfig_quiet) {
2336        System.out.println();
2337      }
2338      // System.out.print("Creating implications "); // XXX untested code
2339      // for (PptTopLevel ppt : all_ppts) {
2340      //   System.out.print('.');
2341      //   ppt.addImplications();
2342      // }
2343      // System.out.println();
2344    } catch (IOException e) {
2345      // System.out.println();
2346      // e.printStackTrace();
2347      throw new Error(e);
2348    } finally {
2349      long duration = System.nanoTime() - startTime;
2350      debugProgress.fine(
2351          "Time spent on read_data_trace_files: " + TimeUnit.NANOSECONDS.toSeconds(duration));
2352    }
2353
2354    if (monitor != null) {
2355      monitor.stop();
2356    }
2357
2358    if (FileIO.dkconfig_read_samples_only) {
2359      throw new Daikon.NormalTermination(
2360          String.format("Finished reading %d samples", FileIO.samples_processed));
2361    }
2362
2363    if (all_ppts.size() == 0) {
2364      String message = "No program point declarations were found.";
2365      if (FileIO.omitted_declarations != 0) {
2366        message +=
2367            lineSep
2368                + "  "
2369                + FileIO.omitted_declarations
2370                + " "
2371                + ((FileIO.omitted_declarations == 1) ? "declaration was" : "declarations were")
2372                + " omitted by regexps (e.g., --ppt-select-pattern).";
2373      }
2374      throw new Daikon.UserError(message);
2375    }
2376
2377    // System.out.println("samples processed: " + FileIO.samples_processed);
2378
2379    int unmatched_count = FileIO.call_stack.size() + FileIO.call_hashmap.size();
2380    if ((use_dataflow_hierarchy && FileIO.samples_processed == unmatched_count)
2381        || (FileIO.samples_processed == 0)) {
2382      throw new Daikon.UserError(
2383          "No samples found for any of " + StringsPlume.nplural(all_ppts.size(), "program point"));
2384    }
2385
2386    // ppt_stats (all_ppts);
2387
2388    //     if (debugStats.isLoggable (FINE)) {
2389    //       PptSliceEquality.print_equality_stats (debugStats, all_ppts);
2390    //     }
2391
2392    // Print equality set info
2393    //     for (PptTopLevel ppt : all_ppts.pptIterable()) {
2394    //       System.out.printf("ppt: %s", ppt.name);
2395    //       if ((ppt.equality_view == null) || (ppt.equality_view.invs == null))
2396    //       continue;
2397    //       for (Invariant inv : ppt.equality_view.invs) {
2398    //       Equality e = (Equality) inv;
2399    //       System.out.printf("    equality set = %s", e);
2400    //       }
2401    //     }
2402
2403    // System.out.printf("printing ternary invariants");
2404    // PrintInvariants.print_all_ternary_invs (all_ppts);
2405    // System.exit(0);
2406
2407    // Postprocessing
2408
2409    debugProgress.fine("Create Combined Exits ... ");
2410    startTime = System.nanoTime();
2411    create_combined_exits(all_ppts);
2412    long duration = System.nanoTime() - startTime;
2413    debugProgress.fine(
2414        "Create Combined Exits ... done [" + TimeUnit.NANOSECONDS.toSeconds(duration) + "]");
2415
2416    // Post process dynamic constants
2417    if (DynamicConstants.dkconfig_use_dynamic_constant_optimization) {
2418      debugProgress.fine("Constant Post Processing ... ");
2419      startTime = System.nanoTime();
2420      for (PptTopLevel ppt : all_ppts.ppt_all_iterable()) {
2421        if (ppt.constants != null) {
2422          ppt.constants.post_process();
2423        }
2424      }
2425      duration = System.nanoTime() - startTime;
2426      debugProgress.fine(
2427          "Constant Post Processing ... done [" + TimeUnit.NANOSECONDS.toSeconds(duration) + "]");
2428    }
2429
2430    // Initialize the partial order hierarchy
2431    debugProgress.fine("Init Hierarchy ... ");
2432    startTime = System.nanoTime();
2433    assert FileIO.new_decl_format != null
2434        : "@AssumeAssertion(nullness): read data, so new_decl_format is set";
2435    if (FileIO.new_decl_format) {
2436      PptRelation.init_hierarchy_new(all_ppts);
2437    } else {
2438      PptRelation.init_hierarchy(all_ppts);
2439    }
2440    duration = System.nanoTime() - startTime;
2441    debugProgress.fine(
2442        "Init Hierarchy ... done [" + TimeUnit.NANOSECONDS.toSeconds(duration) + "]");
2443
2444    // Calculate invariants at all non-leaf ppts
2445    if (use_dataflow_hierarchy) {
2446      debugProgress.fine("createUpperPpts ... ");
2447      startTime = System.nanoTime();
2448      // calculates invariants; does not actually create any ppts
2449      createUpperPpts(all_ppts);
2450      duration = System.nanoTime() - startTime;
2451      debugProgress.fine(
2452          "createUpperPpts ... done [" + TimeUnit.NANOSECONDS.toSeconds(duration) + "]");
2453    }
2454
2455    // Equality data for each PptTopLevel.
2456    if (Daikon.use_equality_optimization && !Daikon.dkconfig_undo_opts) {
2457      debugProgress.fine("Equality Post Process ... ");
2458      startTime = System.nanoTime();
2459      for (PptTopLevel ppt : all_ppts.ppt_all_iterable()) {
2460        // ppt.equality_view can be null here
2461        ppt.postProcessEquality();
2462      }
2463      duration = System.nanoTime() - startTime;
2464      debugProgress.fine(
2465          "Equality Post Process ... done [" + TimeUnit.NANOSECONDS.toSeconds(duration) + "]");
2466    }
2467
2468    // undo optimizations; results in a more redundant but more complete
2469    // set of invariants
2470    if (Daikon.dkconfig_undo_opts) {
2471      undoOpts(all_ppts);
2472    }
2473
2474    // Debug print information about equality sets
2475    if (debugEquality.isLoggable(FINE)) {
2476      for (PptTopLevel ppt : all_ppts.ppt_all_iterable()) {
2477        debugEquality.fine(ppt.name() + ": " + ppt.equality_sets_txt());
2478      }
2479    }
2480
2481    debugProgress.fine("Non-implication postprocessing ... done");
2482
2483    isInferencing = false;
2484
2485    // Add implications
2486    startTime = System.nanoTime();
2487    fileio_progress.clear();
2488    if (!PptSplitter.dkconfig_disable_splitting) {
2489      debugProgress.fine("Adding Implications ... ");
2490      for (PptTopLevel ppt : all_ppts.pptIterable()) {
2491        // debugProgress.fine ("  Adding implications for " + ppt.name);
2492        ppt.addImplications();
2493      }
2494      duration = System.nanoTime() - startTime;
2495      debugProgress.fine(
2496          "Adding Implications ... done [" + TimeUnit.NANOSECONDS.toSeconds(duration) + "]");
2497    }
2498  }
2499
2500  private static class Count {
2501    public int val;
2502
2503    Count(int val) {
2504      this.val = val;
2505    }
2506  }
2507
2508  /** Print out basic statistics (samples, invariants, variables, etc) about each ppt. */
2509  public static void ppt_stats(PptMap all_ppts) {
2510
2511    int all_ppt_cnt = 0;
2512    int ppt_w_sample_cnt = 0;
2513    for (PptTopLevel ppt : all_ppts.pptIterable()) {
2514      all_ppt_cnt++;
2515      if (ppt.num_samples() == 0) {
2516        continue;
2517      }
2518      ppt_w_sample_cnt++;
2519      System.out.printf("%s%n", ppt.name());
2520      System.out.printf("  samples    = %n%d", ppt.num_samples());
2521      System.out.printf("  invariants = %n%d", ppt.invariant_cnt());
2522      Map<ProglangType, Count> type_map = new LinkedHashMap<>();
2523      int leader_cnt = 0;
2524      for (VarInfo v : ppt.var_infos) {
2525        if (!v.isCanonical()) {
2526          continue;
2527        }
2528        leader_cnt++;
2529        Count cnt = type_map.computeIfAbsent(v.file_rep_type, __ -> new Count(0));
2530        cnt.val++;
2531      }
2532      System.out.println("  vars       = " + ppt.var_infos.length);
2533      System.out.println("  leaders    = " + leader_cnt);
2534      for (Map.Entry<@KeyFor("type_map") ProglangType, Count> e : type_map.entrySet()) {
2535        ProglangType file_rep_type = e.getKey();
2536        Count cnt = e.getValue();
2537        System.out.printf("  %s  = %d%n", file_rep_type, cnt.val);
2538      }
2539    }
2540    System.out.println("Total ppt count     = " + all_ppt_cnt);
2541    System.out.println("PPts w/sample count = " + ppt_w_sample_cnt);
2542  }
2543
2544  /** Process the invariants with simplify to remove redundant invariants. */
2545  private static void suppressWithSimplify(PptMap all_ppts) {
2546    System.out.print("Invoking Simplify to identify redundant invariants");
2547    System.out.flush();
2548    long startTime = System.nanoTime();
2549    for (PptTopLevel ppt : all_ppts.ppt_all_iterable()) {
2550      ppt.mark_implied_via_simplify(all_ppts);
2551      System.out.print(".");
2552      System.out.flush();
2553    }
2554    long duration = System.nanoTime() - startTime;
2555    System.out.println(TimeUnit.NANOSECONDS.toSeconds(duration));
2556
2557    // Make sure the Simplify process and helper threads are finished
2558    LemmaStack proverStack = PptTopLevel.getProverStack();
2559    if (proverStack != null) {
2560      proverStack.close();
2561    }
2562  }
2563
2564  /** Initialize NIS suppression. */
2565  @EnsuresNonNull({
2566    "daikon.suppress.NIS.suppressor_map",
2567    "daikon.suppress.NIS.suppressor_map_suppression_count",
2568    "daikon.suppress.NIS.all_suppressions",
2569    "daikon.suppress.NIS.suppressor_proto_invs"
2570  })
2571  public static void setup_NISuppression() {
2572    NIS.init_ni_suppression();
2573  }
2574
2575  /** Initialize the equality sets for each variable. */
2576  public static void setupEquality(PptTopLevel ppt) {
2577
2578    if (!Daikon.use_equality_optimization) {
2579      return;
2580    }
2581
2582    // Skip points that are not leaves.
2583    if (use_dataflow_hierarchy) {
2584      PptTopLevel p = ppt;
2585      if (ppt instanceof PptConditional) {
2586        p = ((PptConditional) ppt).parent;
2587      }
2588
2589      // Rather than defining leaves as :::GLOBAL or :::EXIT54 (numbered
2590      // exit), we define them as everything except
2591      // ::EXIT (combined), :::ENTER, :::THROWS, :::OBJECT
2592      //  and :::CLASS program points.  This scheme ensures that arbitrarly
2593      //  named program points such as :::POINT (used by convertcsv.pl)
2594      //  will be treated as leaves.
2595      if (p.ppt_name.isCombinedExitPoint()
2596          || p.ppt_name.isEnterPoint()
2597          || p.ppt_name.isThrowsPoint()
2598          || p.ppt_name.isObjectInstanceSynthetic()
2599          || p.ppt_name.isClassStaticSynthetic()) {
2600        return;
2601      }
2602
2603      if (ppt.has_splitters()) {
2604        return;
2605      }
2606    }
2607
2608    // Create the initial equality sets
2609    ppt.equality_view = new PptSliceEquality(ppt);
2610    ppt.equality_view.instantiate_invariants();
2611  }
2612
2613  private static List<SpinfoFile> spinfoFiles = new ArrayList<>();
2614
2615  /**
2616   * Create user-defined splitters. For each file in the input, add a SpinfoFile to the spinfoFiles
2617   * variable.
2618   */
2619  public static void create_splitters(Set<File> spinfo_files) throws IOException {
2620    for (File filename : spinfo_files) {
2621      SpinfoFile sf = SplitterFactory.parse_spinfofile(filename);
2622      spinfoFiles.add(sf);
2623    }
2624  }
2625
2626  //   /**
2627  //    * Guard the invariants at all PptTopLevels. Note that this changes
2628  //    * the contents of the PptTopLevels, and the changes made should
2629  //    * probably not be written out to an inv file (save the file before
2630  //    * this is called).
2631  //    */
2632  //   public static void guardInvariants(PptMap allPpts) {
2633  //     for (PptTopLevel ppt : allPpts.asCollection()) {
2634  //       if (ppt.num_samples() == 0)
2635  //         continue;
2636  //       // Make sure isDerivedParam is set before guarding.  Otherwise
2637  //       // we'll never get it correct.
2638  //       for (int iVarInfo = 0;
2639  //         iVarInfo < ppt.var_infos.length;
2640  //         iVarInfo++) {
2641  //         boolean temp =
2642  //           ppt.var_infos[iVarInfo].isDerivedParamAndUninteresting();
2643  //       }
2644  //
2645  //       ppt.guardInvariants();
2646  //     }
2647  //   }
2648
2649  /** Removed invariants as specified in omit_types. */
2650  private static void processOmissions(PptMap allPpts) {
2651    if (omit_types['0']) {
2652      allPpts.removeUnsampled();
2653    }
2654    for (PptTopLevel ppt : allPpts.asCollection()) {
2655      ppt.processOmissions(omit_types);
2656    }
2657  }
2658
2659  /**
2660   * Returns the ppt name, max_ppt, that corresponds to the specified percentage of ppts (presuming
2661   * that only those ppts &le; max_ppt will be processed).
2662   */
2663  private static @Nullable String setup_ppt_perc(Collection<File> decl_files, int ppt_perc) {
2664
2665    // Make sure the percentage is valid
2666    if ((ppt_perc < 1) || (ppt_perc > 100)) {
2667      // The number should already have been checked, so don't use UserError.
2668      throw new BugInDaikon("ppt_perc of " + ppt_perc + " is out of range 1..100");
2669    }
2670    if (ppt_perc == 100) {
2671      return null;
2672    }
2673
2674    // Keep track of all of the ppts in a set ordered by the ppt name
2675    Set<String> ppts = new TreeSet<>();
2676
2677    // Read all of the ppt names out of the decl files
2678    try {
2679      for (File file : decl_files) {
2680
2681        // Open the file
2682        try (LineNumberReader fp = FilesPlume.newLineNumberFileReader(file)) {
2683
2684          // Read each ppt name from the file
2685          for (String line = fp.readLine(); line != null; line = fp.readLine()) {
2686            if (line.equals("") || FileIO.isComment(line)) {
2687              continue;
2688            }
2689            if (!line.equals("DECLARE")) {
2690              continue;
2691            }
2692            // Just read "DECLARE", so next line has ppt name.
2693            String ppt_name = fp.readLine();
2694            if (ppt_name == null) {
2695              throw new Daikon.UserError("File " + file + " terminated prematurely");
2696            }
2697            ppts.add(ppt_name);
2698          }
2699        }
2700      }
2701    } catch (IOException e) {
2702      e.printStackTrace();
2703      throw new Error(e);
2704    }
2705
2706    // Determine the ppt_name that matches the specified percentage.  Always
2707    // return the last exit point from the method (so we don't get half the
2708    // exits from a method or enters without exits, etc)
2709    int ppt_cnt = (ppts.size() * ppt_perc) / 100;
2710    if (ppt_cnt == 0) {
2711      throw new Daikon.UserError(
2712          "ppt_perc of " + ppt_perc + "% results in processing 0 out of " + ppts.size() + " ppts");
2713    }
2714    for (Iterator<String> i = ppts.iterator(); i.hasNext(); ) {
2715      String ppt_name = i.next();
2716      if (--ppt_cnt <= 0) {
2717        String last_ppt_name = ppt_name;
2718        while (i.hasNext()) {
2719          ppt_name = i.next();
2720          if ((last_ppt_name.indexOf("EXIT") != -1) && (ppt_name.indexOf("EXIT") == -1)) {
2721            return last_ppt_name;
2722          }
2723          last_ppt_name = ppt_name;
2724        }
2725        return ppt_name;
2726      }
2727    }
2728    // Execution should not reach this line
2729    throw new Error("ppt_cnt " + ppt_cnt + " ppts.size " + ppts.size());
2730  }
2731
2732  /**
2733   * Undoes the invariants suppressed for the dynamic constant, suppression and equality set
2734   * optimizations (should yield the same invariants as the simple incremental algorithm.
2735   */
2736  @RequiresNonNull({"daikon.suppress.NIS.all_suppressions", "daikon.suppress.NIS.suppressor_map"})
2737  public static void undoOpts(PptMap all_ppts) {
2738
2739    // undo suppressions
2740    for (PptTopLevel ppt : all_ppts.ppt_all_iterable()) {
2741      NIS.create_suppressed_invs(ppt);
2742    }
2743
2744    // undo equality sets
2745    for (PptTopLevel ppt : all_ppts.ppt_all_iterable()) {
2746      PptSliceEquality sliceEquality = ppt.equality_view;
2747
2748      // some program points have no equality sets?
2749      if (sliceEquality == null) {
2750        // System.out.println(ppt.name);
2751        continue;
2752      }
2753
2754      // get the new leaders
2755      List<Equality> allNewInvs = new ArrayList<>();
2756
2757      for (Invariant eq_as_inv : sliceEquality.invs) {
2758        Equality eq = (Equality) eq_as_inv;
2759        VarInfo leader = eq.leader();
2760        List<VarInfo> vars = new ArrayList<>();
2761
2762        for (VarInfo var : eq.getVars()) {
2763          if (!var.equals(leader)) {
2764            vars.add(var);
2765          }
2766        }
2767
2768        if (vars.size() > 0) {
2769
2770          // Create new equality sets for all of the non-equal vars
2771          List<Equality> newInvs = sliceEquality.createEqualityInvs(vars, eq);
2772
2773          // Create new slices and invariants for each new leader
2774          // copyInvsFromLeader(sliceEquality, leader, vars);
2775          sliceEquality.copyInvsFromLeader(leader, vars);
2776
2777          // Keep track of all of the new invariants created.
2778          // Add all of the new equality sets to our list
2779          allNewInvs.addAll(newInvs);
2780        }
2781      }
2782
2783      sliceEquality.invs.addAll(allNewInvs);
2784    }
2785  }
2786}