001package daikon; 002 003import daikon.inv.Invariant; 004import java.util.Arrays; 005import java.util.List; 006import java.util.Set; 007import java.util.logging.Level; 008import java.util.logging.Logger; 009import org.checkerframework.checker.initialization.qual.UnknownInitialization; 010import org.checkerframework.checker.nullness.qual.NonNull; 011import org.checkerframework.checker.nullness.qual.Nullable; 012import org.plumelib.util.ArraysPlume; 013 014/** 015 * Debug class used with the logger to create standardized output. It can be setup to track 016 * combinations of classes, program points, and variables. The most common class to track is an 017 * invariant, but any class can be used. 018 * 019 * <p>This allows detailed information about a particular class/ppt/variable combination to be 020 * printed without getting lost in a mass of other information (which is a particular problem in 021 * Daikon due to the volume of data considered). 022 * 023 * <p>Note that each of the three items (class, ppt, variable) must match in order for printing to 024 * occur. 025 */ 026public final class Debug { 027 028 /** Debug Logger. */ 029 public static final Logger debugTrack = Logger.getLogger("daikon.Debug"); 030 031 /** 032 * List of classes for logging. Each name listed is compared to the fully qualified class name. If 033 * it matches (shows up anywhere in the class name) it will be included in debug prints. This is 034 * not a regular expression match 035 * 036 * @see #log(Logger, Class, Ppt, String) 037 */ 038 public static String[] debugTrackClass = { 039 // "Bound", 040 // "DynamicConstants", 041 // "EltNonZero", 042 // "EltNonZeroFloat", 043 // "EltOneOf", 044 // "Equality", 045 // "FunctionBinary", 046 // "IntEqual", 047 // "IntGreaterEqual", 048 // "IntGreaterThan", 049 // "IntLessEqual", 050 // "IntLessThan", 051 // "IntNonEqual", 052 // "LinearBinary", 053 // "LowerBound", 054 // "Member", 055 // "NonZero", 056 // "OneOfSequence", 057 // "PptSlice", 058 // "PptSlice2", 059 // "PptSliceEquality", 060 // "PptTopLevel", 061 // "SeqIndexComparison", 062 // "SeqIndexNonEqual", 063 // "SeqIntEqual", 064 // "SeqSeqIntEqual", 065 // "NonZero", 066 // "FunctionBinary", 067 // "OneOfSequence", 068 // "IntLessEqual", 069 // "IntGreaterEqual", 070 // "IntLessThan", 071 // "IntGreaterThan", 072 // "IntNonEqual", 073 // "Member", 074 // "FunctionBinary" 075 // "EltNonZero", 076 // "SeqSeqIntGreaterThan", 077 // "SeqSeqIntLessThan", 078 // "SubSet", 079 // "SuperSet", 080 // "EltOneOf", 081 // "Bound", 082 // "SeqSeqIntLessThan", 083 // "SeqSeqIntGreaterThan", 084 // "OneOf" 085 // "StringEqual", 086 // "StringLessThan", 087 // "StringGreaterThan", 088 // "Modlong_zxy", 089 // "UpperBound", 090 }; 091 092 /** 093 * Restrict function binary prints to the specified method. Implementation is in the 094 * FunctionBinary specific log functions. If null, there is no restriction (all function binary 095 * methods are printed). See Functions.java for a list of function names. 096 */ 097 public static @Nullable String function_binary_method = null 098 // "java.lang.Math.max(" 099 // "java.lang.Math.min(" 100 // "org.plumelib.util.MathPlume.logicalXor(" 101 // "org.plumelib.util.MathPlume.gcd(" 102 ; 103 104 /** 105 * List of Ppts for logging. Each name listed is compared to the full program point name. If it 106 * matches (shows up anywhere in the ppt name) it will be included in the debug prints. This is 107 * not a regular expression match. 108 * 109 * @see #log(Logger, Class, Ppt, String) 110 */ 111 public static String[] debugTrackPpt = { 112 // "DataStructures.DisjSets.unionDisjoint(int, int):::EXIT", 113 // "DataStructures.StackAr.StackAr(int):::EXIT", 114 // "DataStructures.StackAr.makeEmpty():::EXIT", 115 // "DataStructures.StackAr.makeEmpty()V:::ENTER", 116 // "DataStructures.StackAr.top():::EXIT74", 117 // "GLOBAL", 118 // "std.main(int;char **;)int:::EXIT", 119 }; 120 121 /** 122 * List of variable names for logging. Each name listed is compared to each variable in turn. If 123 * each matches exactly it will be included in track debug prints. This is not a regular 124 * expression match. Note that the number of variables must match the slice exactly. 125 * 126 * @see #log(Logger, Class, Ppt, String) 127 */ 128 public static String[][] debugTrackVars = { 129 // {"::printstats"}, 130 // {"misc.Fib.STEPS", "orig(misc.Fib.a)"}, 131 // {"misc.Fib.a", "misc.Fib.STEPS"}, 132 // {"return"}, 133 }; 134 135 // Ordinarily, a client would have to supply a Class, Ppt, and 136 // List<Varinfo> with each call to a log method. But the client can 137 // instead provide those values once (they are cached in these variables) 138 // and omit them in subsequent calls. The subsequent calls can use forms 139 // of log() that take fewer arguments. 140 141 /** 142 * True if the cached values should be printed --- that is, they match what is currently being 143 * debugged. 144 */ 145 public boolean cache_match = true; 146 147 // Note that throughout this file, inv_class is not necessarily a 148 // subclass of Invariant -- for instance, it might be a subclass of 149 // BinaryDerivationFactory. 150 /** cached class: class to use by default when calling variants of log() with few arguments. */ 151 public @Nullable Class<?> cache_class; 152 153 /** Cached ppt: ppt to use by default when calling variants of log() with few arguments. */ 154 public @Nullable Ppt cache_ppt; 155 156 /** 157 * Cached variables: variables to use by default when calling variants of log() with few 158 * arguments. 159 */ 160 public VarInfo @Nullable [] cache_vis; 161 162 /** 163 * Ordinarily, a client would have to supply a Class, Ppt, and {@code List<Varinfo>} with each 164 * call to a log method. This constructor sets as defaults c, ppt, and whatever variable (if any) 165 * from vis that is on the debugTrackVar list. Essentially this creates a debug object that will 166 * print if any of the variables in vis are being tracked (and c and ppt match). 167 */ 168 public Debug(Class<?> c, Ppt ppt, VarInfo[] vis) { 169 set(c, ppt, vis); 170 } 171 172 /** 173 * Returns a Debug object if the specified class, ppt, and vis match what is being tracked. 174 * Otherwise, return NULL. Preferred over calling the constructor directly, since it doesn't 175 * create the object if it doesn't have to. 176 */ 177 public static @Nullable Debug newDebug(Class<?> c, Ppt ppt, VarInfo[] vis) { 178 if (logOn() && class_match(c) && ppt_match(ppt) && var_match(vis)) { 179 return new Debug(c, ppt, vis); 180 } else { 181 return null; 182 } 183 } 184 185 /** 186 * Ordinarily, a client would have to supply a Class, Ppt, and {@code List<Varinfo>} with each 187 * call to a log method. This constructor sets as defaults c, ppt, and whatever variable (if any) 188 * from vis that is on the debugTrackVar list. Essentially this creates a debug object that will 189 * print if any of the variables in vis are being tracked (and c and ppt match). 190 */ 191 public Debug(Class<?> c, Ppt ppt, List<VarInfo> vis) { 192 193 VarInfo v = visTracked(vis); 194 if (v != null) { 195 set(c, ppt, new VarInfo[] {v}); 196 } else if (vis.size() > 0) { 197 set(c, ppt, new VarInfo[] {vis.get(0)}); 198 } else { 199 set(c, ppt, null); 200 } 201 } 202 203 /** 204 * Looks for each of the variables in vis in the DebugTrackVar list. If any match, returns that 205 * variable. Returns null if there are no matches. 206 */ 207 public @Nullable VarInfo visTracked(@UnknownInitialization Debug this, List<VarInfo> vis) { 208 209 for (VarInfo v : vis) { 210 Set<VarInfo> evars = null; 211 if (v.equalitySet != null) { 212 evars = v.equalitySet.getVars(); 213 } 214 if (evars != null) { 215 for (VarInfo ev : evars) { 216 for (int k = 0; k < debugTrackVars.length; k++) { 217 if (ev.name().equals(debugTrackVars[k][0])) { 218 return v; 219 } 220 } 221 } 222 } 223 } 224 225 return null; 226 } 227 228 private static @Nullable String[] ourvars = new String[3]; 229 230 private static final VarInfo[] vis1 = new VarInfo[1]; 231 private static final VarInfo[] vis2 = new VarInfo[2]; 232 private static final VarInfo[] vis3 = new VarInfo[3]; 233 234 public static VarInfo[] vis(VarInfo v1) { 235 vis1[0] = v1; 236 return vis1; 237 } 238 239 public static VarInfo[] vis(VarInfo v1, VarInfo v2) { 240 vis2[0] = v1; 241 vis2[1] = v2; 242 return vis2; 243 } 244 245 public static VarInfo[] vis(VarInfo v1, VarInfo v2, VarInfo v3) { 246 vis3[0] = v1; 247 vis3[1] = v2; 248 vis3[2] = v3; 249 return vis3; 250 } 251 252 /** 253 * Sets the cache for class, ppt, and vis so that future calls to log don't have to set them -- in 254 * other words, future calls can use the versions of log with fewer arguments. 255 */ 256 void set( 257 @UnknownInitialization Debug this, 258 @Nullable Class<?> c, 259 @Nullable Ppt ppt, 260 VarInfo @Nullable [] vis) { 261 cache_class = c; 262 cache_ppt = ppt; 263 cache_vis = vis; 264 if (c == null) { 265 System.out.println("Class = null"); 266 } 267 if (ppt == null) { 268 System.out.println("ppt = null"); 269 } 270 if (vis == null) { 271 System.out.println("vis = null"); 272 } else { 273 for (int i = 0; i < vis.length; i++) { 274 if (vis[i] == null) { 275 System.out.println("vis[" + i + "] == null"); 276 } 277 } 278 } 279 cache_match = class_match(c) && ppt_match(ppt) && var_match(vis); 280 } 281 282 /** 283 * When true, perform detailed internal checking. These are essentially additional, possibly 284 * costly assert statements. 285 */ 286 public static boolean dkconfig_internal_check = false; 287 288 /** If true, show stack traces for errors such as file format errors. */ 289 public static boolean dkconfig_show_stack_trace = false; 290 291 /** 292 * Determines whether or not traceback information is printed for each call to log. 293 * 294 * @see #log(Logger, Class, Ppt, String) 295 */ 296 public static boolean dkconfig_showTraceback = false; 297 298 /** 299 * Determines whether or not detailed info (such as from {@code add_modified}) is printed. 300 * 301 * @see #log(Logger, Class, Ppt, String) 302 * @see #logDetail() 303 */ 304 public static boolean dkconfig_logDetail = false; 305 306 /** 307 * Returns whether or not detailed logging is on. Note that this check is not performed inside the 308 * logging calls themselves, it must be performed by the caller. 309 * 310 * @see #log(Logger, Class, Ppt, String) 311 * @see #logOn() 312 */ 313 public static final boolean logDetail() { 314 return dkconfig_logDetail && debugTrack.isLoggable(Level.FINE); 315 } 316 317 /** 318 * Returns whether or not logging is on. 319 * 320 * @see #log(Logger, Class, Ppt, String) 321 */ 322 public static final boolean logOn() { 323 return debugTrack.isLoggable(Level.FINE); 324 } 325 326 /** 327 * Logs the cached class, cached ppt, cached variables and the specified msg via the logger as 328 * described in {@link #log(Logger, Class, Ppt, VarInfo[], String)}. 329 */ 330 public void log(Logger debug, String msg) { 331 if (cache_match) { 332 log(debug, cache_class, cache_ppt, cache_vis, msg); 333 } 334 } 335 336 /** 337 * Logs a description of the class, ppt, ppt variables and the specified msg via the logger as 338 * described in {@link #log(Logger, Class, Ppt, VarInfo[], String)}. 339 */ 340 public static void log(Logger debug, Class<?> inv_class, @Nullable Ppt ppt, String msg) { 341 if (ppt == null) { 342 log(debug, inv_class, ppt, null, msg); 343 } else { 344 log(debug, inv_class, ppt, ppt.var_infos, msg); 345 } 346 } 347 348 /** 349 * Logs a description of the class, ppt, variables and the specified msg via the logger. The 350 * class, ppt, and variables are checked against those described in {@link #debugTrackClass}, 351 * {@link #debugTrackPpt}, and {@link #debugTrackVars}. Only those that match are printed. 352 * Variables will match if they are in the same equality set. The information is written as: 353 * 354 * <p>{@code class: ppt : var1 : var2 : var3 : msg } 355 * 356 * <p>Note that if {@link #debugTrack} is not enabled then nothing is printed. It is somewhat 357 * faster to check {@link #logOn()} directly rather than relying on the check here. 358 * 359 * <p>Other versions of this method (noted below) work without the Logger parameter and take 360 * class, ppt, and vis from the cached values, which were set by the constructor or by the set() 361 * method. 362 * 363 * @param debug a second Logger to query if debug tracking is turned off or does not match. If 364 * this logger is enabled, the same information will be written to it. Note that the 365 * information is never written to both loggers. 366 * @param inv_class the class. Can be obtained in a static context by ClassName.class 367 * @param ppt program point 368 * @param vis variables at the program point. These are sometimes different from the ones in the 369 * ppt itself. 370 * @param msg string message to log 371 * @see #logOn() 372 * @see #logDetail() 373 * @see #log(Class, Ppt, VarInfo[], String) 374 * @see #log(Class, Ppt, String) 375 * @see #log(Logger, String) 376 * @see #log(String) 377 */ 378 public static void log( 379 Logger debug, 380 @Nullable Class<?> inv_class, 381 @Nullable Ppt ppt, 382 VarInfo @Nullable [] vis, 383 String msg) { 384 385 // Try to log via the logger first 386 if (log(inv_class, ppt, vis, msg)) { 387 return; 388 } 389 390 // If debug isn't turned on, there is nothing to do 391 if (!debug.isLoggable(Level.FINE)) { 392 return; 393 } 394 395 // Get the non-qualified class name 396 String class_str; 397 if (inv_class == null) { 398 // when is inv_class null? 399 class_str = "null"; 400 } else { 401 @SuppressWarnings("nullness") // getPackage(): invariant class always has a package 402 @NonNull String packageName = inv_class.getPackage().getName() + "."; 403 class_str = inv_class.getName().replace(packageName, ""); 404 } 405 406 String vars = ""; 407 if (vis == null) { 408 System.out.println("no var infos"); 409 } else { 410 411 // Get a string with all of the variable names. Each is separated by ': ' 412 // 3 variable slots are always setup for consistency 413 for (VarInfo v : vis) { 414 vars += v.name() + ": "; 415 } 416 for (int i = vis.length; i < 3; i++) { 417 vars += ": "; 418 } 419 } 420 421 // Figure out the sample count if possible 422 String samp_str = ""; 423 if (ppt instanceof PptSlice) { 424 PptSlice pslice = (PptSlice) ppt; 425 samp_str = " s" + pslice.num_samples(); 426 } 427 428 String line = " line=" + FileIO.get_linenum(); 429 430 String name = "ppt is null"; 431 if (ppt != null) { 432 name = ppt.name(); 433 } 434 435 debug.fine(class_str + ": " + name + samp_str + line + ": " + vars + msg); 436 if (dkconfig_showTraceback) { 437 Throwable stack = new Throwable("debug traceback"); 438 stack.fillInStackTrace(); 439 stack.printStackTrace(); 440 } 441 } 442 443 /** 444 * Logs a description of the cached class, ppt, and variables and the specified msg via the logger 445 * as described in {@link #log(Logger, Class, Ppt, VarInfo[], String)} 446 * 447 * @return whether or not it logged anything 448 */ 449 public boolean log(String msg) { 450 if (!logOn()) { 451 return false; 452 } 453 return log(cache_class, cache_ppt, cache_vis, msg); 454 } 455 456 /** 457 * Logs a description of the class, ppt, ppt variables and the specified msg via the logger as 458 * described in {@link #log(Logger, Class, Ppt, VarInfo[], String)}. 459 * 460 * @return whether or not it logged anything 461 */ 462 // 3-argument form 463 public static boolean log( 464 Class<?> inv_class, @UnknownInitialization(PptTopLevel.class) Ppt ppt, String msg) { 465 466 return log(inv_class, ppt, ppt.var_infos, msg); 467 } 468 469 /** 470 * Logs a description of the class, ppt, variables and the specified msg via the logger as 471 * described in {@link #log(Logger, Class, Ppt, String)}. Accepts vis because sometimes the 472 * variables are different from those in the ppt. 473 * 474 * @param inv_class the class to be logged 475 * @param ppt the program point to be logged 476 * @param vis the VarInfos for the ppt 477 * @param msg the message to be logged 478 * @return true if it logged something 479 */ 480 // 4-argument form 481 public static boolean log( 482 @Nullable Class<?> inv_class, 483 @Nullable @UnknownInitialization(PptTopLevel.class) Ppt ppt, 484 VarInfo @Nullable [] vis, 485 String msg) { 486 487 if (!debugTrack.isLoggable(Level.FINE)) { 488 return false; 489 } 490 491 // Make sure the class matches 492 if (!class_match(inv_class)) { 493 return false; 494 } 495 496 // Make sure the Ppt matches 497 if (!ppt_match(ppt)) { 498 return false; 499 } 500 501 // Make sure the variables match 502 if (!var_match(vis)) { 503 return false; 504 } 505 506 // Get the non-qualified class name 507 String class_str = "null"; 508 if (inv_class != null) { 509 @SuppressWarnings("nullness") // getPackage(): invariant class always has a package 510 @NonNull String packageName = inv_class.getPackage().getName() + "."; 511 class_str = inv_class.getName().replace(packageName, ""); 512 } 513 514 // Get a string with all of the variable names. Each is separated by ': '. 515 // 3 variable slots are always setup for consistency. 516 String vars = ""; 517 if (vis != null) { 518 int numvars = vis.length; 519 for (int i = 0; i < numvars; i++) { 520 VarInfo v = vis[i]; 521 vars += v.name(); 522 if (ourvars[i] != null) { 523 vars += " {" + ourvars[i] + "}"; 524 } 525 vars += ": "; 526 } 527 for (int i = numvars; i < 3; i++) { 528 vars += ": "; 529 } 530 } 531 532 // Figure out the sample count if possible 533 String samp_str = ""; 534 if (ppt instanceof PptSlice) { 535 PptSlice pslice = (PptSlice) ppt; 536 samp_str = " s" + pslice.num_samples(); 537 } 538 539 String line = " line=" + FileIO.get_linenum(); 540 541 debugTrack.fine( 542 class_str 543 + ": " 544 + ((ppt == null) ? "null" : ppt.name()) 545 + samp_str 546 + line 547 + ": " 548 + vars 549 + msg); 550 if (dkconfig_showTraceback) { 551 Throwable stack = new Throwable("debug traceback"); 552 stack.fillInStackTrace(); 553 stack.printStackTrace(); 554 } 555 556 return true; 557 } 558 559 /** Returns whether or not the specified class matches the classes being tracked. */ 560 public static boolean class_match(@Nullable Class<?> inv_class) { 561 562 if ((debugTrackClass.length > 0) && (inv_class != null)) { 563 return strContainsElem(inv_class.getName(), debugTrackClass); 564 } 565 return true; 566 } 567 568 /** Returns whether or not the specified ppt matches the ppts being tracked. */ 569 public static boolean ppt_match( 570 @Nullable @UnknownInitialization(daikon.PptTopLevel.class) Ppt ppt) { 571 572 if (debugTrackPpt.length > 0) { 573 return (ppt != null) && strContainsElem(ppt.name(), debugTrackPpt); 574 } 575 return true; 576 } 577 578 /** 579 * Returns whether or not the specified vars match the ones being tracked. Also, sets 580 * Debug.ourvars with the names of the variables matched if they are not the leader of their 581 * 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 preceeded 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 String vars_out = ""; 888 for (int ii = 0; ii < debugTrackVars.length; ii++) { 889 vars_out += Arrays.toString(debugTrackVars[ii]) + " "; 890 } 891 debugTrack.fine("Track Vars: " + vars_out); 892 debugTrack.fine("Track Ppts: " + ArraysPlume.toString(debugTrackPpt, false)); 893 894 return null; 895 } 896}