001package daikon; 002 003import daikon.inv.Invariant; 004import java.util.Arrays; 005import java.util.List; 006import java.util.Set; 007import java.util.StringJoiner; 008import java.util.logging.Level; 009import java.util.logging.Logger; 010import org.checkerframework.checker.initialization.qual.UnknownInitialization; 011import org.checkerframework.checker.nullness.qual.NonNull; 012import org.checkerframework.checker.nullness.qual.Nullable; 013import org.plumelib.util.ArraysPlume; 014 015/** 016 * Debug class used with the logger to create standardized output. It can be setup to track 017 * combinations of classes, program points, and variables. The most common class to track is an 018 * invariant, but any class can be used. 019 * 020 * <p>This allows detailed information about a particular class/ppt/variable combination to be 021 * printed without getting lost in a mass of other information (which is a particular problem in 022 * Daikon due to the volume of data considered). 023 * 024 * <p>Note that each of the three items (class, ppt, variable) must match in order for printing to 025 * occur. 026 */ 027public final class Debug { 028 029 /** Debug Logger. */ 030 public static final Logger debugTrack = Logger.getLogger("daikon.Debug"); 031 032 /** 033 * List of classes for logging. Each name listed is compared to the fully qualified class name. If 034 * it matches (shows up anywhere in the class name) it will be included in debug prints. This is 035 * not a regular expression match 036 * 037 * @see #log(Logger, Class, Ppt, String) 038 */ 039 public static String[] debugTrackClass = { 040 // "Bound", 041 // "DynamicConstants", 042 // "EltNonZero", 043 // "EltNonZeroFloat", 044 // "EltOneOf", 045 // "Equality", 046 // "FunctionBinary", 047 // "IntEqual", 048 // "IntGreaterEqual", 049 // "IntGreaterThan", 050 // "IntLessEqual", 051 // "IntLessThan", 052 // "IntNonEqual", 053 // "LinearBinary", 054 // "LowerBound", 055 // "Member", 056 // "NonZero", 057 // "OneOfSequence", 058 // "PptSlice", 059 // "PptSlice2", 060 // "PptSliceEquality", 061 // "PptTopLevel", 062 // "SeqIndexComparison", 063 // "SeqIndexNonEqual", 064 // "SeqIntEqual", 065 // "SeqSeqIntEqual", 066 // "NonZero", 067 // "FunctionBinary", 068 // "OneOfSequence", 069 // "IntLessEqual", 070 // "IntGreaterEqual", 071 // "IntLessThan", 072 // "IntGreaterThan", 073 // "IntNonEqual", 074 // "Member", 075 // "FunctionBinary" 076 // "EltNonZero", 077 // "SeqSeqIntGreaterThan", 078 // "SeqSeqIntLessThan", 079 // "SubSet", 080 // "SuperSet", 081 // "EltOneOf", 082 // "Bound", 083 // "SeqSeqIntLessThan", 084 // "SeqSeqIntGreaterThan", 085 // "OneOf" 086 // "StringEqual", 087 // "StringLessThan", 088 // "StringGreaterThan", 089 // "Modlong_zxy", 090 // "UpperBound", 091 }; 092 093 /** 094 * Restrict function binary prints to the specified method. Implementation is in the 095 * FunctionBinary specific log functions. If null, there is no restriction (all function binary 096 * methods are printed). See Functions.java for a list of function names. 097 */ 098 public static @Nullable String function_binary_method = null 099 // "java.lang.Math.max(" 100 // "java.lang.Math.min(" 101 // "org.plumelib.util.MathPlume.logicalXor(" 102 // "org.plumelib.util.MathPlume.gcd(" 103 ; 104 105 /** 106 * List of Ppts for logging. Each name listed is compared to the full program point name. If it 107 * matches (shows up anywhere in the ppt name) it will be included in the debug prints. This is 108 * not a regular expression match. 109 * 110 * @see #log(Logger, Class, Ppt, String) 111 */ 112 public static String[] debugTrackPpt = { 113 // "DataStructures.DisjSets.unionDisjoint(int, int):::EXIT", 114 // "DataStructures.StackAr.StackAr(int):::EXIT", 115 // "DataStructures.StackAr.makeEmpty():::EXIT", 116 // "DataStructures.StackAr.makeEmpty()V:::ENTER", 117 // "DataStructures.StackAr.top():::EXIT74", 118 // "GLOBAL", 119 // "std.main(int;char **;)int:::EXIT", 120 }; 121 122 /** 123 * List of variable names for logging. Each name listed is compared to each variable in turn. If 124 * each matches exactly it will be included in track debug prints. This is not a regular 125 * expression match. Note that the number of variables must match the slice exactly. 126 * 127 * @see #log(Logger, Class, Ppt, String) 128 */ 129 public static String[][] debugTrackVars = { 130 // {"::printstats"}, 131 // {"misc.Fib.STEPS", "orig(misc.Fib.a)"}, 132 // {"misc.Fib.a", "misc.Fib.STEPS"}, 133 // {"return"}, 134 }; 135 136 // Ordinarily, a client would have to supply a Class, Ppt, and 137 // List<Varinfo> with each call to a log method. But the client can 138 // instead provide those values once (they are cached in these variables) 139 // and omit them in subsequent calls. The subsequent calls can use forms 140 // of log() that take fewer arguments. 141 142 /** 143 * True if the cached values should be printed --- that is, they match what is currently being 144 * debugged. 145 */ 146 public boolean cache_match = true; 147 148 // Note that throughout this file, inv_class is not necessarily a 149 // subclass of Invariant -- for instance, it might be a subclass of 150 // BinaryDerivationFactory. 151 /** cached class: class to use by default when calling variants of log() with few arguments. */ 152 public @Nullable Class<?> cache_class; 153 154 /** Cached ppt: ppt to use by default when calling variants of log() with few arguments. */ 155 public @Nullable Ppt cache_ppt; 156 157 /** 158 * Cached variables: variables to use by default when calling variants of log() with few 159 * arguments. 160 */ 161 public VarInfo @Nullable [] cache_vis; 162 163 /** 164 * Ordinarily, a client would have to supply a Class, Ppt, and {@code List<Varinfo>} with each 165 * call to a log method. This constructor sets as defaults c, ppt, and whatever variable (if any) 166 * from vis that is on the debugTrackVar list. Essentially this creates a debug object that will 167 * print if any of the variables in vis are being tracked (and c and ppt match). 168 */ 169 public Debug(Class<?> c, Ppt ppt, VarInfo[] vis) { 170 set(c, ppt, vis); 171 } 172 173 /** 174 * Returns a Debug object if the specified class, ppt, and vis match what is being tracked. 175 * Otherwise, return NULL. Preferred over calling the constructor directly, since it doesn't 176 * create the object if it doesn't have to. 177 */ 178 public static @Nullable Debug newDebug(Class<?> c, Ppt ppt, VarInfo[] vis) { 179 if (logOn() && class_match(c) && ppt_match(ppt) && var_match(vis)) { 180 return new Debug(c, ppt, vis); 181 } else { 182 return null; 183 } 184 } 185 186 /** 187 * Ordinarily, a client would have to supply a Class, Ppt, and {@code List<Varinfo>} with each 188 * call to a log method. This constructor sets as defaults c, ppt, and whatever variable (if any) 189 * from vis that is on the debugTrackVar list. Essentially this creates a debug object that will 190 * print if any of the variables in vis are being tracked (and c and ppt match). 191 */ 192 public Debug(Class<?> c, Ppt ppt, List<VarInfo> vis) { 193 194 VarInfo v = visTracked(vis); 195 if (v != null) { 196 set(c, ppt, new VarInfo[] {v}); 197 } else if (!vis.isEmpty()) { 198 set(c, ppt, new VarInfo[] {vis.get(0)}); 199 } else { 200 set(c, ppt, null); 201 } 202 } 203 204 /** 205 * Looks for each of the variables in vis in the DebugTrackVar list. If any match, returns that 206 * variable. Returns null if there are no matches. 207 */ 208 public @Nullable VarInfo visTracked(@UnknownInitialization Debug this, List<VarInfo> vis) { 209 210 for (VarInfo v : vis) { 211 Set<VarInfo> evars = null; 212 if (v.equalitySet != null) { 213 evars = v.equalitySet.getVars(); 214 } 215 if (evars != null) { 216 for (VarInfo ev : evars) { 217 for (int k = 0; k < debugTrackVars.length; k++) { 218 if (ev.name().equals(debugTrackVars[k][0])) { 219 return v; 220 } 221 } 222 } 223 } 224 } 225 226 return null; 227 } 228 229 private static @Nullable String[] ourvars = new String[3]; 230 231 private static final VarInfo[] vis1 = new VarInfo[1]; 232 private static final VarInfo[] vis2 = new VarInfo[2]; 233 private static final VarInfo[] vis3 = new VarInfo[3]; 234 235 public static VarInfo[] vis(VarInfo v1) { 236 vis1[0] = v1; 237 return vis1; 238 } 239 240 public static VarInfo[] vis(VarInfo v1, VarInfo v2) { 241 vis2[0] = v1; 242 vis2[1] = v2; 243 return vis2; 244 } 245 246 public static VarInfo[] vis(VarInfo v1, VarInfo v2, VarInfo v3) { 247 vis3[0] = v1; 248 vis3[1] = v2; 249 vis3[2] = v3; 250 return vis3; 251 } 252 253 /** 254 * Sets the cache for class, ppt, and vis so that future calls to log don't have to set them -- in 255 * other words, future calls can use the versions of log with fewer arguments. 256 */ 257 void set( 258 @UnknownInitialization Debug this, 259 @Nullable Class<?> c, 260 @Nullable Ppt ppt, 261 VarInfo @Nullable [] vis) { 262 cache_class = c; 263 cache_ppt = ppt; 264 cache_vis = vis; 265 if (c == null) { 266 System.out.println("Class = null"); 267 } 268 if (ppt == null) { 269 System.out.println("ppt = null"); 270 } 271 if (vis == null) { 272 System.out.println("vis = null"); 273 } else { 274 for (int i = 0; i < vis.length; i++) { 275 if (vis[i] == null) { 276 System.out.println("vis[" + i + "] == null"); 277 } 278 } 279 } 280 cache_match = class_match(c) && ppt_match(ppt) && var_match(vis); 281 } 282 283 /** 284 * When true, perform detailed internal checking. These are essentially additional, possibly 285 * costly assert statements. 286 */ 287 public static boolean dkconfig_internal_check = false; 288 289 /** If true, show stack traces for errors such as file format errors. */ 290 public static boolean dkconfig_show_stack_trace = false; 291 292 /** 293 * Returns true if traceback information is printed for each call to log. 294 * 295 * @see #log(Logger, Class, Ppt, String) 296 */ 297 public static boolean dkconfig_showTraceback = false; 298 299 /** 300 * Returns true if detailed info (such as from {@code add_modified}) is printed. 301 * 302 * @see #log(Logger, Class, Ppt, String) 303 * @see #logDetail() 304 */ 305 public static boolean dkconfig_logDetail = false; 306 307 /** 308 * Returns true if detailed logging is on. Note that this check is not performed inside the 309 * logging calls themselves, it must be performed by the caller. 310 * 311 * @see #log(Logger, Class, Ppt, String) 312 * @see #logOn() 313 */ 314 public static final boolean logDetail() { 315 return dkconfig_logDetail && debugTrack.isLoggable(Level.FINE); 316 } 317 318 /** 319 * Returns true if logging is on. 320 * 321 * @see #log(Logger, Class, Ppt, String) 322 */ 323 public static final boolean logOn() { 324 return debugTrack.isLoggable(Level.FINE); 325 } 326 327 /** 328 * Logs the cached class, cached ppt, cached variables and the specified msg via the logger as 329 * described in {@link #log(Logger, Class, Ppt, VarInfo[], String)}. 330 */ 331 public void log(Logger debug, String msg) { 332 if (cache_match) { 333 log(debug, cache_class, cache_ppt, cache_vis, msg); 334 } 335 } 336 337 /** 338 * Logs a description of the class, ppt, ppt variables and the specified msg via the logger as 339 * described in {@link #log(Logger, Class, Ppt, VarInfo[], String)}. 340 */ 341 public static void log(Logger debug, Class<?> inv_class, @Nullable Ppt ppt, String msg) { 342 if (ppt == null) { 343 log(debug, inv_class, ppt, null, msg); 344 } else { 345 log(debug, inv_class, ppt, ppt.var_infos, msg); 346 } 347 } 348 349 /** 350 * Logs a description of the class, ppt, variables and the specified msg via the logger. The 351 * class, ppt, and variables are checked against those described in {@link #debugTrackClass}, 352 * {@link #debugTrackPpt}, and {@link #debugTrackVars}. Only those that match are printed. 353 * Variables will match if they are in the same equality set. The information is written as: 354 * 355 * <p>{@code class: ppt : var1 : var2 : var3 : msg } 356 * 357 * <p>Note that if {@link #debugTrack} is not enabled then nothing is printed. It is somewhat 358 * faster to check {@link #logOn()} directly rather than relying on the check here. 359 * 360 * <p>Other versions of this method (noted below) work without the Logger parameter and take 361 * class, ppt, and vis from the cached values, which were set by the constructor or by the set() 362 * method. 363 * 364 * @param debug a second Logger to query if debug tracking is turned off or does not match. If 365 * this logger is enabled, the same information will be written to it. Note that the 366 * information is never written to both loggers. 367 * @param inv_class the class. Can be obtained in a static context by ClassName.class 368 * @param ppt program point 369 * @param vis variables at the program point. These are sometimes different from the ones in the 370 * ppt itself. 371 * @param msg string message to log 372 * @see #logOn() 373 * @see #logDetail() 374 * @see #log(Class, Ppt, VarInfo[], String) 375 * @see #log(Class, Ppt, String) 376 * @see #log(Logger, String) 377 * @see #log(String) 378 */ 379 public static void log( 380 Logger debug, 381 @Nullable Class<?> inv_class, 382 @Nullable Ppt ppt, 383 VarInfo @Nullable [] vis, 384 String msg) { 385 386 // Try to log via the logger first 387 if (log(inv_class, ppt, vis, msg)) { 388 return; 389 } 390 391 // If debug isn't turned on, there is nothing to do 392 if (!debug.isLoggable(Level.FINE)) { 393 return; 394 } 395 396 // Get the non-qualified class name 397 String class_str; 398 if (inv_class == null) { 399 // when is inv_class null? 400 class_str = "null"; 401 } else { 402 @SuppressWarnings("nullness") // getPackage(): invariant class always has a package 403 @NonNull String packageName = inv_class.getPackage().getName() + "."; 404 class_str = inv_class.getName().replace(packageName, ""); 405 } 406 407 String vars = ""; 408 if (vis == null) { 409 System.out.println("no var infos"); 410 } else { 411 412 // Get a string with all of the variable names. Each is separated by ': ' 413 // 3 variable slots are always setup for consistency 414 for (VarInfo v : vis) { 415 vars += v.name() + ": "; 416 } 417 for (int i = vis.length; i < 3; i++) { 418 vars += ": "; 419 } 420 } 421 422 // Figure out the sample count if possible 423 String samp_str = ""; 424 if (ppt instanceof PptSlice) { 425 PptSlice pslice = (PptSlice) ppt; 426 samp_str = " s" + pslice.num_samples(); 427 } 428 429 String line = " line=" + FileIO.get_linenum(); 430 431 String name = "ppt is null"; 432 if (ppt != null) { 433 name = ppt.name(); 434 } 435 436 debug.fine(class_str + ": " + name + samp_str + line + ": " + vars + msg); 437 if (dkconfig_showTraceback) { 438 Throwable stack = new Throwable("debug traceback"); 439 stack.fillInStackTrace(); 440 stack.printStackTrace(); 441 } 442 } 443 444 /** 445 * Logs a description of the cached class, ppt, and variables and the specified msg via the logger 446 * as described in {@link #log(Logger, Class, Ppt, VarInfo[], String)} 447 * 448 * @return true if it logged anything 449 */ 450 public boolean log(String msg) { 451 if (!logOn()) { 452 return false; 453 } 454 return log(cache_class, cache_ppt, cache_vis, msg); 455 } 456 457 /** 458 * Logs a description of the class, ppt, ppt variables and the specified msg via the logger as 459 * described in {@link #log(Logger, Class, Ppt, VarInfo[], String)}. 460 * 461 * @return true if it logged anything 462 */ 463 // 3-argument form 464 public static boolean log( 465 Class<?> inv_class, @UnknownInitialization(PptTopLevel.class) Ppt ppt, String msg) { 466 467 return log(inv_class, ppt, ppt.var_infos, msg); 468 } 469 470 /** 471 * Logs a description of the class, ppt, variables and the specified msg via the logger as 472 * described in {@link #log(Logger, Class, Ppt, String)}. Accepts vis because sometimes the 473 * variables are different from those in the ppt. 474 * 475 * @param inv_class the class to be logged 476 * @param ppt the program point to be logged 477 * @param vis the VarInfos for the ppt 478 * @param msg the message to be logged 479 * @return true if it logged something 480 */ 481 // 4-argument form 482 public static boolean log( 483 @Nullable Class<?> inv_class, 484 @Nullable @UnknownInitialization(PptTopLevel.class) Ppt ppt, 485 VarInfo @Nullable [] vis, 486 String msg) { 487 488 if (!debugTrack.isLoggable(Level.FINE)) { 489 return false; 490 } 491 492 // Make sure the class matches 493 if (!class_match(inv_class)) { 494 return false; 495 } 496 497 // Make sure the Ppt matches 498 if (!ppt_match(ppt)) { 499 return false; 500 } 501 502 // Make sure the variables match 503 if (!var_match(vis)) { 504 return false; 505 } 506 507 // Get the non-qualified class name 508 String class_str = "null"; 509 if (inv_class != null) { 510 @SuppressWarnings("nullness") // getPackage(): invariant class always has a package 511 @NonNull String packageName = inv_class.getPackage().getName() + "."; 512 class_str = inv_class.getName().replace(packageName, ""); 513 } 514 515 // Get a string with all of the variable names. Each is separated by ': '. 516 // 3 variable slots are always setup for consistency. 517 String vars = ""; 518 if (vis != null) { 519 int numvars = vis.length; 520 for (int i = 0; i < numvars; i++) { 521 VarInfo v = vis[i]; 522 vars += v.name(); 523 if (ourvars[i] != null) { 524 vars += " {" + ourvars[i] + "}"; 525 } 526 vars += ": "; 527 } 528 for (int i = numvars; i < 3; i++) { 529 vars += ": "; 530 } 531 } 532 533 // Figure out the sample count if possible 534 String samp_str = ""; 535 if (ppt instanceof PptSlice) { 536 PptSlice pslice = (PptSlice) ppt; 537 samp_str = " s" + pslice.num_samples(); 538 } 539 540 String line = " line=" + FileIO.get_linenum(); 541 542 debugTrack.fine( 543 class_str 544 + ": " 545 + ((ppt == null) ? "null" : ppt.name()) 546 + samp_str 547 + line 548 + ": " 549 + vars 550 + msg); 551 if (dkconfig_showTraceback) { 552 Throwable stack = new Throwable("debug traceback"); 553 stack.fillInStackTrace(); 554 stack.printStackTrace(); 555 } 556 557 return true; 558 } 559 560 /** Returns true if the specified class matches the classes being tracked. */ 561 public static boolean class_match(@Nullable Class<?> inv_class) { 562 563 if ((debugTrackClass.length > 0) && (inv_class != null)) { 564 return strContainsElem(inv_class.getName(), debugTrackClass); 565 } 566 return true; 567 } 568 569 /** Returns true if the specified ppt matches the ppts being tracked. */ 570 public static boolean ppt_match( 571 @Nullable @UnknownInitialization(daikon.PptTopLevel.class) Ppt ppt) { 572 573 if (debugTrackPpt.length > 0) { 574 return (ppt != null) && strContainsElem(ppt.name(), debugTrackPpt); 575 } 576 return true; 577 } 578 579 /** 580 * Returns true if the specified vars match the ones being tracked. Also, sets Debug.ourvars with 581 * the names of the variables matched if they are not the leader of their equality sets. 582 */ 583 public static boolean var_match(VarInfo @Nullable [] vis) { 584 585 if (debugTrackVars.length == 0) { 586 return true; 587 } 588 if (vis == null) { 589 return false; 590 } 591 592 boolean match = false; 593 594 // Loop through each set of specified debug variables. 595 outer: 596 for (String[] cv : debugTrackVars) { 597 if (cv.length != vis.length) { 598 continue; 599 } 600 for (int j = 0; j < ourvars.length; j++) { 601 ourvars[j] = null; 602 } 603 604 // Flags to insure that we don't match a variable more than once 605 boolean[] used = {false, false, false}; 606 607 // Loop through each variable in this set of debug variables 608 for (int j = 0; j < cv.length; j++) { 609 boolean this_match = false; 610 611 // Loop through each variable at this point 612 eachvis: 613 for (int k = 0; k < vis.length; k++) { 614 615 // Get the matching equality set 616 Set<VarInfo> evars = null; 617 if (vis[k].equalitySet != null) { 618 evars = vis[k].equalitySet.getVars(); 619 } 620 621 // If there is an equality set 622 if ((evars != null) && vis[k].isCanonical()) { 623 624 // Loop through each variable in the equality set 625 for (VarInfo v : evars) { 626 if (!used[k] && (cv[j].equals("*") || cv[j].equals(v.name()))) { 627 used[k] = true; 628 this_match = true; 629 if (!cv[j].equals(vis[j].name())) { 630 ourvars[j] = v.name(); 631 if (j != k) { 632 ourvars[j] += " (" + j + "/" + k + ")"; 633 } 634 if (v.isCanonical()) { 635 ourvars[j] += " (Leader)"; 636 } 637 } 638 break eachvis; 639 } 640 } 641 } else { // sometimes, no equality set 642 if (cv[j].equals("*") || cv[j].equals(vis[k].name())) { 643 this_match = true; 644 } 645 } 646 } 647 if (!this_match) { 648 continue outer; 649 } 650 } 651 match = true; 652 break outer; 653 } 654 655 return match; 656 } 657 658 /** Looks for an element in arr that is a substring of str. */ 659 private static boolean strContainsElem(String str, String[] arr) { 660 661 for (String elt : arr) { 662 if (str.indexOf(elt) >= 0) { 663 return true; 664 } 665 } 666 return false; 667 } 668 669 /** 670 * Looks through entire ppt tree and checks for any items we are interested in. If found, prints 671 * them out. 672 */ 673 public static void check(PptMap all_ppts, String msg) { 674 675 boolean found = false; 676 677 for (PptTopLevel ppt : all_ppts.ppt_all_iterable()) { 678 if (ppt_match(ppt)) { 679 debugTrack.fine("Matched ppt '" + ppt.name() + "' at " + msg); 680 } 681 for (PptSlice slice : ppt.views_iterable()) { 682 for (int k = 0; k < slice.invs.size(); k++) { 683 Invariant inv = slice.invs.get(k); 684 if (inv.log("%s: found #%s=%s in slice %s", msg, k, inv.format(), slice)) { 685 found = true; 686 } 687 } 688 } 689 } 690 if (!found) { 691 debugTrack.fine("Found no points at '" + msg + "'"); 692 } 693 } 694 695 /** Returns a string containing the integer variables and their values. */ 696 public static String int_vars(PptTopLevel ppt, ValueTuple vt) { 697 698 String out = ""; 699 700 for (VarInfo v : ppt.var_infos) { 701 if (!v.isCanonical()) { 702 continue; 703 } 704 if (v.file_rep_type != ProglangType.INT) { 705 continue; 706 } 707 out += v.name() + "=" + toString(v.getValueOrNull(vt)) + " [" + vt.getModified(v) + "]: "; 708 } 709 return out; 710 } 711 712 /** 713 * Returns a string containing the variable values for any variables that are currently being 714 * tracked in ppt. The string is of the form 'v1 = val1: v2 = val2, etc. 715 */ 716 public static String related_vars(PptTopLevel ppt, ValueTuple vt) { 717 718 String out = ""; 719 720 for (VarInfo v : ppt.var_infos) { 721 for (String[] cv : debugTrackVars) { 722 for (String cv_elt : cv) { 723 if (cv_elt.equals(v.name())) { 724 Object val = v.getValue(vt); 725 int mod = vt.getModified(v); 726 out += v.name() + "="; 727 out += toString(val); 728 if ((mod == ValueTuple.MISSING_FLOW) || (mod == ValueTuple.MISSING_NONSENSICAL)) { 729 out += " (missing)"; 730 } 731 if (v.missingOutOfBounds()) { 732 out += " (out of bounds)"; 733 } 734 if (v.equalitySet != null) { 735 if (!v.isCanonical()) { 736 out += " (leader=" + v.canonicalRep().name() + ")"; 737 } 738 } 739 // out += " mod=" + mod; 740 out += ": "; 741 } 742 } 743 } 744 } 745 746 return out; 747 } 748 749 /** Like Object.toString(), but handles null, and has special handling for arrays. */ 750 public static String toString(@Nullable Object val) { 751 if (val == null) { 752 return "none"; 753 } 754 if (val instanceof String) { 755 return "\"" + val + "\""; 756 } 757 if (val instanceof VarInfo[]) { 758 return Arrays.toString((VarInfo[]) val); 759 } 760 if (val instanceof String[]) { 761 return Arrays.toString((String[]) val); 762 } 763 if (val instanceof boolean[]) { 764 return Arrays.toString((boolean[]) val); 765 } 766 if (val instanceof byte[]) { 767 return Arrays.toString((byte[]) val); 768 } 769 if (val instanceof char[]) { 770 return Arrays.toString((char[]) val); 771 } 772 if (val instanceof double[]) { 773 return Arrays.toString((double[]) val); 774 } 775 if (val instanceof float[]) { 776 return Arrays.toString((float[]) val); 777 } 778 if (val instanceof int[]) { 779 return Arrays.toString((int[]) val); 780 } 781 if (val instanceof long[]) { 782 return Arrays.toString((long[]) val); 783 } 784 if (val instanceof short[]) { 785 return Arrays.toString((short[]) val); 786 } 787 return val.toString(); 788 } 789 790 /** 791 * Returns a string containing each variable and its value. 792 * 793 * <p>The string is of the form "v1 = val1: v2 = val2, ...". 794 */ 795 public static String toString(VarInfo[] vis, ValueTuple vt) { 796 797 StringBuilder out = new StringBuilder(); 798 799 for (VarInfo v : vis) { 800 Object val = v.getValue(vt); 801 // int mod = vt.getModified(v); 802 out.append(v.name()); 803 out.append("="); 804 out.append(toString(val)); 805 if (v.isMissing(vt)) { 806 out.append(" (missing)"); 807 } 808 if (v.missingOutOfBounds()) { 809 out.append(" (out of bounds)"); 810 } 811 if (v.equalitySet != null) { 812 if (!v.isCanonical()) { 813 out.append(" (leader=" + v.canonicalRep().name() + ")"); 814 } 815 } 816 out.append(": "); 817 } 818 819 return out.toString(); 820 } 821 822 /** 823 * Parses the specified argument to {@code --track} and sets up the track arrays accordingly. The 824 * syntax of the argument is 825 * 826 * <pre>{@code class|class|...<var,var,var>@ppt}</pre> 827 * 828 * As shown, multiple class arguments can be specified separated by pipe symbols (|). The 829 * variables are specified in angle brackets ({@code <>}) and the program point is preceded by an 830 * at sign (@). Each is optional and can be left out. The add_track routine can be called multiple 831 * times. An invariant that matches any of the specifications will be tracked. 832 */ 833 public static @Nullable String add_track(String def) { 834 835 String classes = null; 836 String vars = null; 837 String ppt = null; 838 839 // Get the classes, vars, and ppt 840 int var_start = def.indexOf('<'); 841 int ppt_start = def.indexOf('@'); 842 if ((var_start == -1) && (ppt_start == -1)) { 843 classes = def; 844 } else if (var_start != -1) { 845 if (var_start > 0) { 846 classes = def.substring(0, var_start); 847 } 848 if (ppt_start == -1) { 849 vars = def.substring(var_start + 1, def.length() - 1); 850 } else { 851 vars = def.substring(var_start + 1, ppt_start - 1); 852 ppt = def.substring(ppt_start + 1, def.length()); 853 } 854 } else { 855 if (ppt_start > 0) { 856 classes = def.substring(0, ppt_start); 857 } 858 ppt = def.substring(ppt_start + 1, def.length()); 859 } 860 861 // If classes were specified, get each class 862 if (classes != null) { 863 String[] class_arr = classes.split("\\|"); 864 debugTrackClass = ArraysPlume.concat(debugTrackClass, class_arr); 865 } 866 867 // If vars were specified, get each var 868 if (vars != null) { 869 String[] var_arr = vars.split(", *"); 870 String[][] new_var = new String[debugTrackVars.length + 1][]; 871 for (int ii = 0; ii < debugTrackVars.length; ii++) { 872 new_var[ii] = debugTrackVars[ii]; 873 } 874 new_var[debugTrackVars.length] = var_arr; 875 debugTrackVars = new_var; 876 } 877 878 // if a ppt was specified, add it to the array of tracked ppts 879 if (ppt != null) { 880 String[] newPpt = new String[] {ppt}; 881 debugTrackPpt = ArraysPlume.concat(debugTrackPpt, newPpt); 882 } 883 884 System.out.println(); 885 debugTrack.fine("After --track: " + def); 886 debugTrack.fine("Track Classes: " + ArraysPlume.toString(debugTrackClass, false)); 887 StringJoiner vars_out = new StringJoiner(" "); 888 for (String[] cv : debugTrackVars) { 889 vars_out.add(Arrays.toString(cv)); 890 } 891 debugTrack.fine("Track Vars: " + vars_out); 892 debugTrack.fine("Track Ppts: " + ArraysPlume.toString(debugTrackPpt, false)); 893 894 return null; 895 } 896}