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) ⇒ (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 * @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 ≤ 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}