1 /* 2 * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * This source code is provided to illustrate the usage of a given feature 28 * or technique and has been deliberately simplified. Additional steps 29 * required for a production-quality application, such as security checks, 30 * input validation and proper error handling, might not be present in 31 * this sample code. 32 */ 33 34 35 package com.sun.tools.example.debug.tty; 36 37 import com.sun.jdi.*; 38 import com.sun.jdi.connect.Connector; 39 import com.sun.jdi.request.*; 40 import com.sun.tools.example.debug.expr.ExpressionParser; 41 import com.sun.tools.example.debug.expr.ParseException; 42 43 import java.text.*; 44 import java.util.*; 45 import java.io.*; 46 47 class Commands { 48 49 abstract class AsyncExecution { 50 abstract void action(); 51 52 AsyncExecution() { 53 execute(); 54 } 55 56 void execute() { 57 /* 58 * Save current thread and stack frame. (BugId 4296031) 59 */ 60 final ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); 61 final int stackFrame = threadInfo == null? 0 : threadInfo.getCurrentFrameIndex(); 62 Thread thread = new Thread("asynchronous jdb command") { 63 @Override 64 public void run() { 65 try { 66 action(); 67 } catch (UnsupportedOperationException uoe) { 68 //(BugId 4453329) 69 MessageOutput.println("Operation is not supported on the target VM"); 70 } catch (Exception e) { 71 MessageOutput.println("Internal exception during operation:", 72 e.getMessage()); 73 } finally { 74 /* 75 * This was an asynchronous command. Events may have been 76 * processed while it was running. Restore the thread and 77 * stack frame the user was looking at. (BugId 4296031) 78 */ 79 if (threadInfo != null) { 80 ThreadInfo.setCurrentThreadInfo(threadInfo); 81 try { 82 threadInfo.setCurrentFrameIndex(stackFrame); 83 } catch (IncompatibleThreadStateException e) { 84 MessageOutput.println("Current thread isnt suspended."); 85 } catch (ArrayIndexOutOfBoundsException e) { 86 MessageOutput.println("Requested stack frame is no longer active:", 87 new Object []{new Integer(stackFrame)}); 88 } 89 } 90 MessageOutput.printPrompt(); 91 } 92 } 93 }; 94 thread.start(); 95 } 96 } 97 98 Commands() { 99 } 100 101 private Value evaluate(String expr) { 102 Value result = null; 103 ExpressionParser.GetFrame frameGetter = null; 104 try { 105 final ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); 106 if ((threadInfo != null) && (threadInfo.getCurrentFrame() != null)) { 107 frameGetter = new ExpressionParser.GetFrame() { 108 @Override 109 public StackFrame get() throws IncompatibleThreadStateException { 110 return threadInfo.getCurrentFrame(); 111 } 112 }; 113 } 114 result = ExpressionParser.evaluate(expr, Env.vm(), frameGetter); 115 } catch (InvocationException ie) { 116 MessageOutput.println("Exception in expression:", 117 ie.exception().referenceType().name()); 118 } catch (Exception ex) { 119 String exMessage = ex.getMessage(); 120 if (exMessage == null) { 121 MessageOutput.printException(exMessage, ex); 122 } else { 123 String s; 124 try { 125 s = MessageOutput.format(exMessage); 126 } catch (MissingResourceException mex) { 127 s = ex.toString(); 128 } 129 MessageOutput.printDirectln(s);// Special case: use printDirectln() 130 } 131 } 132 return result; 133 } 134 135 private String getStringValue() { 136 Value val = null; 137 String valStr = null; 138 try { 139 val = ExpressionParser.getMassagedValue(); 140 valStr = val.toString(); 141 } catch (ParseException e) { 142 String msg = e.getMessage(); 143 if (msg == null) { 144 MessageOutput.printException(msg, e); 145 } else { 146 String s; 147 try { 148 s = MessageOutput.format(msg); 149 } catch (MissingResourceException mex) { 150 s = e.toString(); 151 } 152 MessageOutput.printDirectln(s); 153 } 154 } 155 return valStr; 156 } 157 158 private ThreadInfo doGetThread(String idToken) { 159 ThreadInfo threadInfo = ThreadInfo.getThreadInfo(idToken); 160 if (threadInfo == null) { 161 MessageOutput.println("is not a valid thread id", idToken); 162 } 163 return threadInfo; 164 } 165 166 String typedName(Method method) { 167 StringBuilder sb = new StringBuilder(); 168 sb.append(method.name()); 169 sb.append("("); 170 171 List<String> args = method.argumentTypeNames(); 172 int lastParam = args.size() - 1; 173 // output param types except for the last 174 for (int ii = 0; ii < lastParam; ii++) { 175 sb.append(args.get(ii)); 176 sb.append(", "); 177 } 178 if (lastParam >= 0) { 179 // output the last param 180 String lastStr = args.get(lastParam); 181 if (method.isVarArgs()) { 182 // lastParam is an array. Replace the [] with ... 183 sb.append(lastStr.substring(0, lastStr.length() - 2)); 184 sb.append("..."); 185 } else { 186 sb.append(lastStr); 187 } 188 } 189 sb.append(")"); 190 return sb.toString(); 191 } 192 193 void commandConnectors(VirtualMachineManager vmm) { 194 Collection<Connector> ccs = vmm.allConnectors(); 195 if (ccs.isEmpty()) { 196 MessageOutput.println("Connectors available"); 197 } 198 for (Connector cc : ccs) { 199 String transportName = 200 cc.transport() == null ? "null" : cc.transport().name(); 201 MessageOutput.println(); 202 MessageOutput.println("Connector and Transport name", 203 new Object [] {cc.name(), transportName}); 204 MessageOutput.println("Connector description", cc.description()); 205 206 for (Connector.Argument aa : cc.defaultArguments().values()) { 207 MessageOutput.println(); 208 209 boolean requiredArgument = aa.mustSpecify(); 210 if (aa.value() == null || aa.value() == "") { 211 //no current value and no default. 212 MessageOutput.println(requiredArgument ? 213 "Connector required argument nodefault" : 214 "Connector argument nodefault", aa.name()); 215 } else { 216 MessageOutput.println(requiredArgument ? 217 "Connector required argument default" : 218 "Connector argument default", 219 new Object [] {aa.name(), aa.value()}); 220 } 221 MessageOutput.println("Connector description", aa.description()); 222 223 } 224 } 225 226 } 227 228 void commandClasses() { 229 StringBuilder classList = new StringBuilder(); 230 for (ReferenceType refType : Env.vm().allClasses()) { 231 classList.append(refType.name()); 232 classList.append("\n"); 233 } 234 MessageOutput.print("** classes list **", classList.toString()); 235 } 236 237 void commandClass(StringTokenizer t) { 238 239 if (!t.hasMoreTokens()) { 240 MessageOutput.println("No class specified."); 241 return; 242 } 243 244 String idClass = t.nextToken(); 245 boolean showAll = false; 246 247 if (t.hasMoreTokens()) { 248 if (t.nextToken().toLowerCase().equals("all")) { 249 showAll = true; 250 } else { 251 MessageOutput.println("Invalid option on class command"); 252 return; 253 } 254 } 255 ReferenceType type = Env.getReferenceTypeFromToken(idClass); 256 if (type == null) { 257 MessageOutput.println("is not a valid id or class name", idClass); 258 return; 259 } 260 if (type instanceof ClassType) { 261 ClassType clazz = (ClassType)type; 262 MessageOutput.println("Class:", clazz.name()); 263 264 ClassType superclass = clazz.superclass(); 265 while (superclass != null) { 266 MessageOutput.println("extends:", superclass.name()); 267 superclass = showAll ? superclass.superclass() : null; 268 } 269 270 List<InterfaceType> interfaces = 271 showAll ? clazz.allInterfaces() : clazz.interfaces(); 272 for (InterfaceType interfaze : interfaces) { 273 MessageOutput.println("implements:", interfaze.name()); 274 } 275 276 for (ClassType sub : clazz.subclasses()) { 277 MessageOutput.println("subclass:", sub.name()); 278 } 279 for (ReferenceType nest : clazz.nestedTypes()) { 280 MessageOutput.println("nested:", nest.name()); 281 } 282 } else if (type instanceof InterfaceType) { 283 InterfaceType interfaze = (InterfaceType)type; 284 MessageOutput.println("Interface:", interfaze.name()); 285 for (InterfaceType superinterface : interfaze.superinterfaces()) { 286 MessageOutput.println("extends:", superinterface.name()); 287 } 288 for (InterfaceType sub : interfaze.subinterfaces()) { 289 MessageOutput.println("subinterface:", sub.name()); 290 } 291 for (ClassType implementor : interfaze.implementors()) { 292 MessageOutput.println("implementor:", implementor.name()); 293 } 294 for (ReferenceType nest : interfaze.nestedTypes()) { 295 MessageOutput.println("nested:", nest.name()); 296 } 297 } else { // array type 298 ArrayType array = (ArrayType)type; 299 MessageOutput.println("Array:", array.name()); 300 } 301 } 302 303 void commandMethods(StringTokenizer t) { 304 if (!t.hasMoreTokens()) { 305 MessageOutput.println("No class specified."); 306 return; 307 } 308 309 String idClass = t.nextToken(); 310 ReferenceType cls = Env.getReferenceTypeFromToken(idClass); 311 if (cls != null) { 312 StringBuilder methodsList = new StringBuilder(); 313 for (Method method : cls.allMethods()) { 314 methodsList.append(method.declaringType().name()); 315 methodsList.append(" "); 316 methodsList.append(typedName(method)); 317 methodsList.append('\n'); 318 } 319 MessageOutput.print("** methods list **", methodsList.toString()); 320 } else { 321 MessageOutput.println("is not a valid id or class name", idClass); 322 } 323 } 324 325 void commandFields(StringTokenizer t) { 326 if (!t.hasMoreTokens()) { 327 MessageOutput.println("No class specified."); 328 return; 329 } 330 331 String idClass = t.nextToken(); 332 ReferenceType cls = Env.getReferenceTypeFromToken(idClass); 333 if (cls != null) { 334 List<Field> fields = cls.allFields(); 335 List<Field> visible = cls.visibleFields(); 336 StringBuilder fieldsList = new StringBuilder(); 337 for (Field field : fields) { 338 String s; 339 if (!visible.contains(field)) { 340 s = MessageOutput.format("list field typename and name hidden", 341 new Object [] {field.typeName(), 342 field.name()}); 343 } else if (!field.declaringType().equals(cls)) { 344 s = MessageOutput.format("list field typename and name inherited", 345 new Object [] {field.typeName(), 346 field.name(), 347 field.declaringType().name()}); 348 } else { 349 s = MessageOutput.format("list field typename and name", 350 new Object [] {field.typeName(), 351 field.name()}); 352 } 353 fieldsList.append(s); 354 } 355 MessageOutput.print("** fields list **", fieldsList.toString()); 356 } else { 357 MessageOutput.println("is not a valid id or class name", idClass); 358 } 359 } 360 361 private void printThreadGroup(ThreadGroupReference tg) { 362 ThreadIterator threadIter = new ThreadIterator(tg); 363 364 MessageOutput.println("Thread Group:", tg.name()); 365 int maxIdLength = 0; 366 int maxNameLength = 0; 367 while (threadIter.hasNext()) { 368 ThreadReference thr = threadIter.next(); 369 maxIdLength = Math.max(maxIdLength, 370 Env.description(thr).length()); 371 maxNameLength = Math.max(maxNameLength, 372 thr.name().length()); 373 } 374 375 threadIter = new ThreadIterator(tg); 376 while (threadIter.hasNext()) { 377 ThreadReference thr = threadIter.next(); 378 if (thr.threadGroup() == null) { 379 continue; 380 } 381 // Note any thread group changes 382 if (!thr.threadGroup().equals(tg)) { 383 tg = thr.threadGroup(); 384 MessageOutput.println("Thread Group:", tg.name()); 385 } 386 387 /* 388 * Do a bit of filling with whitespace to get thread ID 389 * and thread names to line up in the listing, and also 390 * allow for proper localization. This also works for 391 * very long thread names, at the possible cost of lines 392 * being wrapped by the display device. 393 */ 394 StringBuilder idBuffer = new StringBuilder(Env.description(thr)); 395 for (int i = idBuffer.length(); i < maxIdLength; i++) { 396 idBuffer.append(" "); 397 } 398 StringBuilder nameBuffer = new StringBuilder(thr.name()); 399 for (int i = nameBuffer.length(); i < maxNameLength; i++) { 400 nameBuffer.append(" "); 401 } 402 403 /* 404 * Select the output format to use based on thread status 405 * and breakpoint. 406 */ 407 String statusFormat; 408 switch (thr.status()) { 409 case ThreadReference.THREAD_STATUS_UNKNOWN: 410 if (thr.isAtBreakpoint()) { 411 statusFormat = "Thread description name unknownStatus BP"; 412 } else { 413 statusFormat = "Thread description name unknownStatus"; 414 } 415 break; 416 case ThreadReference.THREAD_STATUS_ZOMBIE: 417 if (thr.isAtBreakpoint()) { 418 statusFormat = "Thread description name zombieStatus BP"; 419 } else { 420 statusFormat = "Thread description name zombieStatus"; 421 } 422 break; 423 case ThreadReference.THREAD_STATUS_RUNNING: 424 if (thr.isAtBreakpoint()) { 425 statusFormat = "Thread description name runningStatus BP"; 426 } else { 427 statusFormat = "Thread description name runningStatus"; 428 } 429 break; 430 case ThreadReference.THREAD_STATUS_SLEEPING: 431 if (thr.isAtBreakpoint()) { 432 statusFormat = "Thread description name sleepingStatus BP"; 433 } else { 434 statusFormat = "Thread description name sleepingStatus"; 435 } 436 break; 437 case ThreadReference.THREAD_STATUS_MONITOR: 438 if (thr.isAtBreakpoint()) { 439 statusFormat = "Thread description name waitingStatus BP"; 440 } else { 441 statusFormat = "Thread description name waitingStatus"; 442 } 443 break; 444 case ThreadReference.THREAD_STATUS_WAIT: 445 if (thr.isAtBreakpoint()) { 446 statusFormat = "Thread description name condWaitstatus BP"; 447 } else { 448 statusFormat = "Thread description name condWaitstatus"; 449 } 450 break; 451 default: 452 throw new InternalError(MessageOutput.format("Invalid thread status.")); 453 } 454 MessageOutput.println(statusFormat, 455 new Object [] {idBuffer.toString(), 456 nameBuffer.toString()}); 457 } 458 } 459 460 void commandThreads(StringTokenizer t) { 461 if (!t.hasMoreTokens()) { 462 printThreadGroup(ThreadInfo.group()); 463 return; 464 } 465 String name = t.nextToken(); 466 ThreadGroupReference tg = ThreadGroupIterator.find(name); 467 if (tg == null) { 468 MessageOutput.println("is not a valid threadgroup name", name); 469 } else { 470 printThreadGroup(tg); 471 } 472 } 473 474 void commandThreadGroups() { 475 ThreadGroupIterator it = new ThreadGroupIterator(); 476 int cnt = 0; 477 while (it.hasNext()) { 478 ThreadGroupReference tg = it.nextThreadGroup(); 479 ++cnt; 480 MessageOutput.println("thread group number description name", 481 new Object [] { new Integer (cnt), 482 Env.description(tg), 483 tg.name()}); 484 } 485 } 486 487 void commandThread(StringTokenizer t) { 488 if (!t.hasMoreTokens()) { 489 MessageOutput.println("Thread number not specified."); 490 return; 491 } 492 ThreadInfo threadInfo = doGetThread(t.nextToken()); 493 if (threadInfo != null) { 494 ThreadInfo.setCurrentThreadInfo(threadInfo); 495 } 496 } 497 498 void commandThreadGroup(StringTokenizer t) { 499 if (!t.hasMoreTokens()) { 500 MessageOutput.println("Threadgroup name not specified."); 501 return; 502 } 503 String name = t.nextToken(); 504 ThreadGroupReference tg = ThreadGroupIterator.find(name); 505 if (tg == null) { 506 MessageOutput.println("is not a valid threadgroup name", name); 507 } else { 508 ThreadInfo.setThreadGroup(tg); 509 } 510 } 511 512 void commandRun(StringTokenizer t) { 513 /* 514 * The 'run' command makes little sense in a 515 * that doesn't support restarts or multiple VMs. However, 516 * this is an attempt to emulate the behavior of the old 517 * JDB as much as possible. For new users and implementations 518 * it is much more straightforward to launch immedidately 519 * with the -launch option. 520 */ 521 VMConnection connection = Env.connection(); 522 if (!connection.isLaunch()) { 523 if (!t.hasMoreTokens()) { 524 commandCont(); 525 } else { 526 MessageOutput.println("run <args> command is valid only with launched VMs"); 527 } 528 return; 529 } 530 if (connection.isOpen()) { 531 MessageOutput.println("VM already running. use cont to continue after events."); 532 return; 533 } 534 535 /* 536 * Set the main class and any arguments. Note that this will work 537 * only with the standard launcher, "com.sun.jdi.CommandLineLauncher" 538 */ 539 String args; 540 if (t.hasMoreTokens()) { 541 args = t.nextToken(""); 542 boolean argsSet = connection.setConnectorArg("main", args); 543 if (!argsSet) { 544 MessageOutput.println("Unable to set main class and arguments"); 545 return; 546 } 547 } else { 548 args = connection.connectorArg("main"); 549 if (args.length() == 0) { 550 MessageOutput.println("Main class and arguments must be specified"); 551 return; 552 } 553 } 554 MessageOutput.println("run", args); 555 556 /* 557 * Launch the VM. 558 */ 559 connection.open(); 560 561 } 562 563 void commandLoad(StringTokenizer t) { 564 MessageOutput.println("The load command is no longer supported."); 565 } 566 567 private List<ThreadReference> allThreads(ThreadGroupReference group) { 568 List<ThreadReference> list = new ArrayList<ThreadReference>(); 569 list.addAll(group.threads()); 570 for (ThreadGroupReference child : group.threadGroups()) { 571 list.addAll(allThreads(child)); 572 } 573 return list; 574 } 575 576 void commandSuspend(StringTokenizer t) { 577 if (!t.hasMoreTokens()) { 578 Env.vm().suspend(); 579 MessageOutput.println("All threads suspended."); 580 } else { 581 while (t.hasMoreTokens()) { 582 ThreadInfo threadInfo = doGetThread(t.nextToken()); 583 if (threadInfo != null) { 584 threadInfo.getThread().suspend(); 585 } 586 } 587 } 588 } 589 590 void commandResume(StringTokenizer t) { 591 if (!t.hasMoreTokens()) { 592 ThreadInfo.invalidateAll(); 593 Env.vm().resume(); 594 MessageOutput.println("All threads resumed."); 595 } else { 596 while (t.hasMoreTokens()) { 597 ThreadInfo threadInfo = doGetThread(t.nextToken()); 598 if (threadInfo != null) { 599 threadInfo.invalidate(); 600 threadInfo.getThread().resume(); 601 } 602 } 603 } 604 } 605 606 void commandCont() { 607 if (ThreadInfo.getCurrentThreadInfo() == null) { 608 MessageOutput.println("Nothing suspended."); 609 return; 610 } 611 ThreadInfo.invalidateAll(); 612 Env.vm().resume(); 613 } 614 615 void clearPreviousStep(ThreadReference thread) { 616 /* 617 * A previous step may not have completed on this thread; 618 * if so, it gets removed here. 619 */ 620 EventRequestManager mgr = Env.vm().eventRequestManager(); 621 for (StepRequest request : mgr.stepRequests()) { 622 if (request.thread().equals(thread)) { 623 mgr.deleteEventRequest(request); 624 break; 625 } 626 } 627 } 628 /* step 629 * 630 */ 631 void commandStep(StringTokenizer t) { 632 ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); 633 if (threadInfo == null) { 634 MessageOutput.println("Nothing suspended."); 635 return; 636 } 637 int depth; 638 if (t.hasMoreTokens() && 639 t.nextToken().toLowerCase().equals("up")) { 640 depth = StepRequest.STEP_OUT; 641 } else { 642 depth = StepRequest.STEP_INTO; 643 } 644 645 clearPreviousStep(threadInfo.getThread()); 646 EventRequestManager reqMgr = Env.vm().eventRequestManager(); 647 StepRequest request = reqMgr.createStepRequest(threadInfo.getThread(), 648 StepRequest.STEP_LINE, depth); 649 if (depth == StepRequest.STEP_INTO) { 650 Env.addExcludes(request); 651 } 652 // We want just the next step event and no others 653 request.addCountFilter(1); 654 request.enable(); 655 ThreadInfo.invalidateAll(); 656 Env.vm().resume(); 657 } 658 659 /* stepi 660 * step instruction. 661 */ 662 void commandStepi() { 663 ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); 664 if (threadInfo == null) { 665 MessageOutput.println("Nothing suspended."); 666 return; 667 } 668 clearPreviousStep(threadInfo.getThread()); 669 EventRequestManager reqMgr = Env.vm().eventRequestManager(); 670 StepRequest request = reqMgr.createStepRequest(threadInfo.getThread(), 671 StepRequest.STEP_MIN, 672 StepRequest.STEP_INTO); 673 Env.addExcludes(request); 674 // We want just the next step event and no others 675 request.addCountFilter(1); 676 request.enable(); 677 ThreadInfo.invalidateAll(); 678 Env.vm().resume(); 679 } 680 681 void commandNext() { 682 ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); 683 if (threadInfo == null) { 684 MessageOutput.println("Nothing suspended."); 685 return; 686 } 687 clearPreviousStep(threadInfo.getThread()); 688 EventRequestManager reqMgr = Env.vm().eventRequestManager(); 689 StepRequest request = reqMgr.createStepRequest(threadInfo.getThread(), 690 StepRequest.STEP_LINE, 691 StepRequest.STEP_OVER); 692 Env.addExcludes(request); 693 // We want just the next step event and no others 694 request.addCountFilter(1); 695 request.enable(); 696 ThreadInfo.invalidateAll(); 697 Env.vm().resume(); 698 } 699 700 void doKill(ThreadReference thread, StringTokenizer t) { 701 if (!t.hasMoreTokens()) { 702 MessageOutput.println("No exception object specified."); 703 return; 704 } 705 String expr = t.nextToken(""); 706 Value val = evaluate(expr); 707 if ((val != null) && (val instanceof ObjectReference)) { 708 try { 709 thread.stop((ObjectReference)val); 710 MessageOutput.println("killed", thread.toString()); 711 } catch (InvalidTypeException e) { 712 MessageOutput.println("Invalid exception object"); 713 } 714 } else { 715 MessageOutput.println("Expression must evaluate to an object"); 716 } 717 } 718 719 void doKillThread(final ThreadReference threadToKill, 720 final StringTokenizer tokenizer) { 721 new AsyncExecution() { 722 @Override 723 void action() { 724 doKill(threadToKill, tokenizer); 725 } 726 }; 727 } 728 729 void commandKill(StringTokenizer t) { 730 if (!t.hasMoreTokens()) { 731 MessageOutput.println("Usage: kill <thread id> <throwable>"); 732 return; 733 } 734 ThreadInfo threadInfo = doGetThread(t.nextToken()); 735 if (threadInfo != null) { 736 MessageOutput.println("killing thread:", threadInfo.getThread().name()); 737 doKillThread(threadInfo.getThread(), t); 738 return; 739 } 740 } 741 742 void listCaughtExceptions() { 743 boolean noExceptions = true; 744 745 // Print a listing of the catch patterns currently in place 746 for (EventRequestSpec spec : Env.specList.eventRequestSpecs()) { 747 if (spec instanceof ExceptionSpec) { 748 if (noExceptions) { 749 noExceptions = false; 750 MessageOutput.println("Exceptions caught:"); 751 } 752 MessageOutput.println("tab", spec.toString()); 753 } 754 } 755 if (noExceptions) { 756 MessageOutput.println("No exceptions caught."); 757 } 758 } 759 760 private EventRequestSpec parseExceptionSpec(StringTokenizer t) { 761 String notification = t.nextToken(); 762 boolean notifyCaught = false; 763 boolean notifyUncaught = false; 764 EventRequestSpec spec = null; 765 String classPattern = null; 766 767 if (notification.equals("uncaught")) { 768 notifyCaught = false; 769 notifyUncaught = true; 770 } else if (notification.equals("caught")) { 771 notifyCaught = true; 772 notifyUncaught = false; 773 } else if (notification.equals("all")) { 774 notifyCaught = true; 775 notifyUncaught = true; 776 } else { 777 /* 778 * Handle the same as "all" for backward 779 * compatibility with existing .jdbrc files. 780 * 781 * Insert an "all" and take the current token as the 782 * intended classPattern 783 * 784 */ 785 notifyCaught = true; 786 notifyUncaught = true; 787 classPattern = notification; 788 } 789 if (classPattern == null && t.hasMoreTokens()) { 790 classPattern = t.nextToken(); 791 } 792 if ((classPattern != null) && (notifyCaught || notifyUncaught)) { 793 try { 794 spec = Env.specList.createExceptionCatch(classPattern, 795 notifyCaught, 796 notifyUncaught); 797 } catch (ClassNotFoundException exc) { 798 MessageOutput.println("is not a valid class name", classPattern); 799 } 800 } 801 return spec; 802 } 803 804 void commandCatchException(StringTokenizer t) { 805 if (!t.hasMoreTokens()) { 806 listCaughtExceptions(); 807 } else { 808 EventRequestSpec spec = parseExceptionSpec(t); 809 if (spec != null) { 810 resolveNow(spec); 811 } else { 812 MessageOutput.println("Usage: catch exception"); 813 } 814 } 815 } 816 817 void commandIgnoreException(StringTokenizer t) { 818 if (!t.hasMoreTokens()) { 819 listCaughtExceptions(); 820 } else { 821 EventRequestSpec spec = parseExceptionSpec(t); 822 if (Env.specList.delete(spec)) { 823 MessageOutput.println("Removed:", spec.toString()); 824 } else { 825 if (spec != null) { 826 MessageOutput.println("Not found:", spec.toString()); 827 } 828 MessageOutput.println("Usage: ignore exception"); 829 } 830 } 831 } 832 833 void commandUp(StringTokenizer t) { 834 ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); 835 if (threadInfo == null) { 836 MessageOutput.println("Current thread not set."); 837 return; 838 } 839 840 int nLevels = 1; 841 if (t.hasMoreTokens()) { 842 String idToken = t.nextToken(); 843 int i; 844 try { 845 NumberFormat nf = NumberFormat.getNumberInstance(); 846 nf.setParseIntegerOnly(true); 847 Number n = nf.parse(idToken); 848 i = n.intValue(); 849 } catch (java.text.ParseException jtpe) { 850 i = 0; 851 } 852 if (i <= 0) { 853 MessageOutput.println("Usage: up [n frames]"); 854 return; 855 } 856 nLevels = i; 857 } 858 859 try { 860 threadInfo.up(nLevels); 861 } catch (IncompatibleThreadStateException e) { 862 MessageOutput.println("Current thread isnt suspended."); 863 } catch (ArrayIndexOutOfBoundsException e) { 864 MessageOutput.println("End of stack."); 865 } 866 } 867 868 void commandDown(StringTokenizer t) { 869 ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); 870 if (threadInfo == null) { 871 MessageOutput.println("Current thread not set."); 872 return; 873 } 874 875 int nLevels = 1; 876 if (t.hasMoreTokens()) { 877 String idToken = t.nextToken(); 878 int i; 879 try { 880 NumberFormat nf = NumberFormat.getNumberInstance(); 881 nf.setParseIntegerOnly(true); 882 Number n = nf.parse(idToken); 883 i = n.intValue(); 884 } catch (java.text.ParseException jtpe) { 885 i = 0; 886 } 887 if (i <= 0) { 888 MessageOutput.println("Usage: down [n frames]"); 889 return; 890 } 891 nLevels = i; 892 } 893 894 try { 895 threadInfo.down(nLevels); 896 } catch (IncompatibleThreadStateException e) { 897 MessageOutput.println("Current thread isnt suspended."); 898 } catch (ArrayIndexOutOfBoundsException e) { 899 MessageOutput.println("End of stack."); 900 } 901 } 902 903 private void dumpStack(ThreadInfo threadInfo, boolean showPC) { 904 List<StackFrame> stack = null; 905 try { 906 stack = threadInfo.getStack(); 907 } catch (IncompatibleThreadStateException e) { 908 MessageOutput.println("Current thread isnt suspended."); 909 return; 910 } 911 if (stack == null) { 912 MessageOutput.println("Thread is not running (no stack)."); 913 } else { 914 int nFrames = stack.size(); 915 for (int i = threadInfo.getCurrentFrameIndex(); i < nFrames; i++) { 916 StackFrame frame = stack.get(i); 917 dumpFrame (i, showPC, frame); 918 } 919 } 920 } 921 922 private void dumpFrame (int frameNumber, boolean showPC, StackFrame frame) { 923 Location loc = frame.location(); 924 long pc = -1; 925 if (showPC) { 926 pc = loc.codeIndex(); 927 } 928 Method meth = loc.method(); 929 930 long lineNumber = loc.lineNumber(); 931 String methodInfo = null; 932 if (meth.isNative()) { 933 methodInfo = MessageOutput.format("native method"); 934 } else if (lineNumber != -1) { 935 try { 936 methodInfo = loc.sourceName() + 937 MessageOutput.format("line number", 938 new Object [] {Long.valueOf(lineNumber)}); 939 } catch (AbsentInformationException e) { 940 methodInfo = MessageOutput.format("unknown"); 941 } 942 } 943 if (pc != -1) { 944 MessageOutput.println("stack frame dump with pc", 945 new Object [] {new Integer(frameNumber + 1), 946 meth.declaringType().name(), 947 meth.name(), 948 methodInfo, 949 Long.valueOf(pc)}); 950 } else { 951 MessageOutput.println("stack frame dump", 952 new Object [] {new Integer(frameNumber + 1), 953 meth.declaringType().name(), 954 meth.name(), 955 methodInfo}); 956 } 957 } 958 959 void commandWhere(StringTokenizer t, boolean showPC) { 960 if (!t.hasMoreTokens()) { 961 ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); 962 if (threadInfo == null) { 963 MessageOutput.println("No thread specified."); 964 return; 965 } 966 dumpStack(threadInfo, showPC); 967 } else { 968 String token = t.nextToken(); 969 if (token.toLowerCase().equals("all")) { 970 for (ThreadInfo threadInfo : ThreadInfo.threads()) { 971 MessageOutput.println("Thread:", 972 threadInfo.getThread().name()); 973 dumpStack(threadInfo, showPC); 974 } 975 } else { 976 ThreadInfo threadInfo = doGetThread(token); 977 if (threadInfo != null) { 978 ThreadInfo.setCurrentThreadInfo(threadInfo); 979 dumpStack(threadInfo, showPC); 980 } 981 } 982 } 983 } 984 985 void commandInterrupt(StringTokenizer t) { 986 if (!t.hasMoreTokens()) { 987 ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); 988 if (threadInfo == null) { 989 MessageOutput.println("No thread specified."); 990 return; 991 } 992 threadInfo.getThread().interrupt(); 993 } else { 994 ThreadInfo threadInfo = doGetThread(t.nextToken()); 995 if (threadInfo != null) { 996 threadInfo.getThread().interrupt(); 997 } 998 } 999 } 1000 1001 void commandMemory() { 1002 MessageOutput.println("The memory command is no longer supported."); 1003 } 1004 1005 void commandGC() { 1006 MessageOutput.println("The gc command is no longer necessary."); 1007 } 1008 1009 /* 1010 * The next two methods are used by this class and by EventHandler 1011 * to print consistent locations and error messages. 1012 */ 1013 static String locationString(Location loc) { 1014 return MessageOutput.format("locationString", 1015 new Object [] {loc.declaringType().name(), 1016 loc.method().name(), 1017 new Integer (loc.lineNumber()), 1018 new Long (loc.codeIndex())}); 1019 } 1020 1021 void listBreakpoints() { 1022 boolean noBreakpoints = true; 1023 1024 // Print set breakpoints 1025 for (EventRequestSpec spec : Env.specList.eventRequestSpecs()) { 1026 if (spec instanceof BreakpointSpec) { 1027 if (noBreakpoints) { 1028 noBreakpoints = false; 1029 MessageOutput.println("Breakpoints set:"); 1030 } 1031 MessageOutput.println("tab", spec.toString()); 1032 } 1033 } 1034 if (noBreakpoints) { 1035 MessageOutput.println("No breakpoints set."); 1036 } 1037 } 1038 1039 1040 private void printBreakpointCommandUsage(String atForm, String inForm) { 1041 MessageOutput.println("printbreakpointcommandusage", 1042 new Object [] {atForm, inForm}); 1043 } 1044 1045 protected BreakpointSpec parseBreakpointSpec(StringTokenizer t, 1046 String atForm, String inForm) { 1047 BreakpointSpec breakpoint = null; 1048 try { 1049 String token = t.nextToken(":( \t\n\r"); 1050 1051 // We can't use hasMoreTokens here because it will cause any leading 1052 // paren to be lost. 1053 String rest; 1054 try { 1055 rest = t.nextToken("").trim(); 1056 } catch (NoSuchElementException e) { 1057 rest = null; 1058 } 1059 1060 if ((rest != null) && rest.startsWith(":")) { 1061 t = new StringTokenizer(rest.substring(1)); 1062 String classId = token; 1063 String lineToken = t.nextToken(); 1064 1065 NumberFormat nf = NumberFormat.getNumberInstance(); 1066 nf.setParseIntegerOnly(true); 1067 Number n = nf.parse(lineToken); 1068 int lineNumber = n.intValue(); 1069 1070 if (t.hasMoreTokens()) { 1071 printBreakpointCommandUsage(atForm, inForm); 1072 return null; 1073 } 1074 try { 1075 breakpoint = Env.specList.createBreakpoint(classId, 1076 lineNumber); 1077 } catch (ClassNotFoundException exc) { 1078 MessageOutput.println("is not a valid class name", classId); 1079 } 1080 } else { 1081 // Try stripping method from class.method token. 1082 int idot = token.lastIndexOf('.'); 1083 if ( (idot <= 0) || /* No dot or dot in first char */ 1084 (idot >= token.length() - 1) ) { /* dot in last char */ 1085 printBreakpointCommandUsage(atForm, inForm); 1086 return null; 1087 } 1088 String methodName = token.substring(idot + 1); 1089 String classId = token.substring(0, idot); 1090 List<String> argumentList = null; 1091 if (rest != null) { 1092 if (!rest.startsWith("(") || !rest.endsWith(")")) { 1093 MessageOutput.println("Invalid method specification:", 1094 methodName + rest); 1095 printBreakpointCommandUsage(atForm, inForm); 1096 return null; 1097 } 1098 // Trim the parens 1099 rest = rest.substring(1, rest.length() - 1); 1100 1101 argumentList = new ArrayList<String>(); 1102 t = new StringTokenizer(rest, ","); 1103 while (t.hasMoreTokens()) { 1104 argumentList.add(t.nextToken()); 1105 } 1106 } 1107 try { 1108 breakpoint = Env.specList.createBreakpoint(classId, 1109 methodName, 1110 argumentList); 1111 } catch (MalformedMemberNameException exc) { 1112 MessageOutput.println("is not a valid method name", methodName); 1113 } catch (ClassNotFoundException exc) { 1114 MessageOutput.println("is not a valid class name", classId); 1115 } 1116 } 1117 } catch (Exception e) { 1118 printBreakpointCommandUsage(atForm, inForm); 1119 return null; 1120 } 1121 return breakpoint; 1122 } 1123 1124 private void resolveNow(EventRequestSpec spec) { 1125 boolean success = Env.specList.addEagerlyResolve(spec); 1126 if (success && !spec.isResolved()) { 1127 MessageOutput.println("Deferring.", spec.toString()); 1128 } 1129 } 1130 1131 void commandStop(StringTokenizer t) { 1132 String atIn; 1133 byte suspendPolicy = EventRequest.SUSPEND_ALL; 1134 1135 if (t.hasMoreTokens()) { 1136 atIn = t.nextToken(); 1137 if (atIn.equals("go") && t.hasMoreTokens()) { 1138 suspendPolicy = EventRequest.SUSPEND_NONE; 1139 atIn = t.nextToken(); 1140 } else if (atIn.equals("thread") && t.hasMoreTokens()) { 1141 suspendPolicy = EventRequest.SUSPEND_EVENT_THREAD; 1142 atIn = t.nextToken(); 1143 } 1144 } else { 1145 listBreakpoints(); 1146 return; 1147 } 1148 1149 BreakpointSpec spec = parseBreakpointSpec(t, "stop at", "stop in"); 1150 if (spec != null) { 1151 // Enforcement of "at" vs. "in". The distinction is really 1152 // unnecessary and we should consider not checking for this 1153 // (and making "at" and "in" optional). 1154 if (atIn.equals("at") && spec.isMethodBreakpoint()) { 1155 MessageOutput.println("Use stop at to set a breakpoint at a line number"); 1156 printBreakpointCommandUsage("stop at", "stop in"); 1157 return; 1158 } 1159 spec.suspendPolicy = suspendPolicy; 1160 resolveNow(spec); 1161 } 1162 } 1163 1164 void commandClear(StringTokenizer t) { 1165 if (!t.hasMoreTokens()) { 1166 listBreakpoints(); 1167 return; 1168 } 1169 1170 BreakpointSpec spec = parseBreakpointSpec(t, "clear", "clear"); 1171 if (spec != null) { 1172 if (Env.specList.delete(spec)) { 1173 MessageOutput.println("Removed:", spec.toString()); 1174 } else { 1175 MessageOutput.println("Not found:", spec.toString()); 1176 } 1177 } 1178 } 1179 1180 private List<WatchpointSpec> parseWatchpointSpec(StringTokenizer t) { 1181 List<WatchpointSpec> list = new ArrayList<WatchpointSpec>(); 1182 boolean access = false; 1183 boolean modification = false; 1184 int suspendPolicy = EventRequest.SUSPEND_ALL; 1185 1186 String fieldName = t.nextToken(); 1187 if (fieldName.equals("go")) { 1188 suspendPolicy = EventRequest.SUSPEND_NONE; 1189 fieldName = t.nextToken(); 1190 } else if (fieldName.equals("thread")) { 1191 suspendPolicy = EventRequest.SUSPEND_EVENT_THREAD; 1192 fieldName = t.nextToken(); 1193 } 1194 if (fieldName.equals("access")) { 1195 access = true; 1196 fieldName = t.nextToken(); 1197 } else if (fieldName.equals("all")) { 1198 access = true; 1199 modification = true; 1200 fieldName = t.nextToken(); 1201 } else { 1202 modification = true; 1203 } 1204 int dot = fieldName.lastIndexOf('.'); 1205 if (dot < 0) { 1206 MessageOutput.println("Class containing field must be specified."); 1207 return list; 1208 } 1209 String className = fieldName.substring(0, dot); 1210 fieldName = fieldName.substring(dot+1); 1211 1212 try { 1213 WatchpointSpec spec; 1214 if (access) { 1215 spec = Env.specList.createAccessWatchpoint(className, 1216 fieldName); 1217 spec.suspendPolicy = suspendPolicy; 1218 list.add(spec); 1219 } 1220 if (modification) { 1221 spec = Env.specList.createModificationWatchpoint(className, 1222 fieldName); 1223 spec.suspendPolicy = suspendPolicy; 1224 list.add(spec); 1225 } 1226 } catch (MalformedMemberNameException exc) { 1227 MessageOutput.println("is not a valid field name", fieldName); 1228 } catch (ClassNotFoundException exc) { 1229 MessageOutput.println("is not a valid class name", className); 1230 } 1231 return list; 1232 } 1233 1234 void commandWatch(StringTokenizer t) { 1235 if (!t.hasMoreTokens()) { 1236 MessageOutput.println("Field to watch not specified"); 1237 return; 1238 } 1239 1240 for (WatchpointSpec spec : parseWatchpointSpec(t)) { 1241 resolveNow(spec); 1242 } 1243 } 1244 1245 void commandUnwatch(StringTokenizer t) { 1246 if (!t.hasMoreTokens()) { 1247 MessageOutput.println("Field to unwatch not specified"); 1248 return; 1249 } 1250 1251 for (WatchpointSpec spec : parseWatchpointSpec(t)) { 1252 if (Env.specList.delete(spec)) { 1253 MessageOutput.println("Removed:", spec.toString()); 1254 } else { 1255 MessageOutput.println("Not found:", spec.toString()); 1256 } 1257 } 1258 } 1259 1260 void turnOnExitTrace(ThreadInfo threadInfo, int suspendPolicy) { 1261 EventRequestManager erm = Env.vm().eventRequestManager(); 1262 MethodExitRequest exit = erm.createMethodExitRequest(); 1263 if (threadInfo != null) { 1264 exit.addThreadFilter(threadInfo.getThread()); 1265 } 1266 Env.addExcludes(exit); 1267 exit.setSuspendPolicy(suspendPolicy); 1268 exit.enable(); 1269 1270 } 1271 1272 static String methodTraceCommand = null; 1273 1274 void commandTrace(StringTokenizer t) { 1275 String modif; 1276 int suspendPolicy = EventRequest.SUSPEND_ALL; 1277 ThreadInfo threadInfo = null; 1278 String goStr = " "; 1279 1280 /* 1281 * trace [go] methods [thread] 1282 * trace [go] method exit | exits [thread] 1283 */ 1284 if (t.hasMoreTokens()) { 1285 modif = t.nextToken(); 1286 if (modif.equals("go")) { 1287 suspendPolicy = EventRequest.SUSPEND_NONE; 1288 goStr = " go "; 1289 if (t.hasMoreTokens()) { 1290 modif = t.nextToken(); 1291 } 1292 } else if (modif.equals("thread")) { 1293 // this is undocumented as it doesn't work right. 1294 suspendPolicy = EventRequest.SUSPEND_EVENT_THREAD; 1295 if (t.hasMoreTokens()) { 1296 modif = t.nextToken(); 1297 } 1298 } 1299 1300 if (modif.equals("method")) { 1301 String traceCmd = null; 1302 1303 if (t.hasMoreTokens()) { 1304 String modif1 = t.nextToken(); 1305 if (modif1.equals("exits") || modif1.equals("exit")) { 1306 if (t.hasMoreTokens()) { 1307 threadInfo = doGetThread(t.nextToken()); 1308 } 1309 if (modif1.equals("exit")) { 1310 StackFrame frame; 1311 try { 1312 frame = ThreadInfo.getCurrentThreadInfo().getCurrentFrame(); 1313 } catch (IncompatibleThreadStateException ee) { 1314 MessageOutput.println("Current thread isnt suspended."); 1315 return; 1316 } 1317 Env.setAtExitMethod(frame.location().method()); 1318 traceCmd = MessageOutput.format("trace" + 1319 goStr + "method exit " + 1320 "in effect for", 1321 Env.atExitMethod().toString()); 1322 } else { 1323 traceCmd = MessageOutput.format("trace" + 1324 goStr + "method exits " + 1325 "in effect"); 1326 } 1327 commandUntrace(new StringTokenizer("methods")); 1328 turnOnExitTrace(threadInfo, suspendPolicy); 1329 methodTraceCommand = traceCmd; 1330 return; 1331 } 1332 } else { 1333 MessageOutput.println("Can only trace"); 1334 return; 1335 } 1336 } 1337 if (modif.equals("methods")) { 1338 // Turn on method entry trace 1339 MethodEntryRequest entry; 1340 EventRequestManager erm = Env.vm().eventRequestManager(); 1341 if (t.hasMoreTokens()) { 1342 threadInfo = doGetThread(t.nextToken()); 1343 } 1344 if (threadInfo != null) { 1345 /* 1346 * To keep things simple we want each 'trace' to cancel 1347 * previous traces. However in this case, we don't do that 1348 * to preserve backward compatibility with pre JDK 6.0. 1349 * IE, you can currently do 1350 * trace methods 0x21 1351 * trace methods 0x22 1352 * and you will get xxx traced just on those two threads 1353 * But this feature is kind of broken because if you then do 1354 * untrace 0x21 1355 * it turns off both traces instead of just the one. 1356 * Another bogosity is that if you do 1357 * trace methods 1358 * trace methods 1359 * and you will get two traces. 1360 */ 1361 1362 entry = erm.createMethodEntryRequest(); 1363 entry.addThreadFilter(threadInfo.getThread()); 1364 } else { 1365 commandUntrace(new StringTokenizer("methods")); 1366 entry = erm.createMethodEntryRequest(); 1367 } 1368 Env.addExcludes(entry); 1369 entry.setSuspendPolicy(suspendPolicy); 1370 entry.enable(); 1371 turnOnExitTrace(threadInfo, suspendPolicy); 1372 methodTraceCommand = MessageOutput.format("trace" + goStr + 1373 "methods in effect"); 1374 1375 return; 1376 } 1377 1378 MessageOutput.println("Can only trace"); 1379 return; 1380 } 1381 1382 // trace all by itself. 1383 if (methodTraceCommand != null) { 1384 MessageOutput.printDirectln(methodTraceCommand); 1385 } 1386 1387 // More trace lines can be added here. 1388 } 1389 1390 void commandUntrace(StringTokenizer t) { 1391 // untrace 1392 // untrace methods 1393 1394 String modif = null; 1395 EventRequestManager erm = Env.vm().eventRequestManager(); 1396 if (t.hasMoreTokens()) { 1397 modif = t.nextToken(); 1398 } 1399 if (modif == null || modif.equals("methods")) { 1400 erm.deleteEventRequests(erm.methodEntryRequests()); 1401 erm.deleteEventRequests(erm.methodExitRequests()); 1402 Env.setAtExitMethod(null); 1403 methodTraceCommand = null; 1404 } 1405 } 1406 1407 void commandList(StringTokenizer t) { 1408 StackFrame frame = null; 1409 ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); 1410 if (threadInfo == null) { 1411 MessageOutput.println("No thread specified."); 1412 return; 1413 } 1414 try { 1415 frame = threadInfo.getCurrentFrame(); 1416 } catch (IncompatibleThreadStateException e) { 1417 MessageOutput.println("Current thread isnt suspended."); 1418 return; 1419 } 1420 1421 if (frame == null) { 1422 MessageOutput.println("No frames on the current call stack"); 1423 return; 1424 } 1425 1426 Location loc = frame.location(); 1427 if (loc.method().isNative()) { 1428 MessageOutput.println("Current method is native"); 1429 return; 1430 } 1431 1432 String sourceFileName = null; 1433 try { 1434 sourceFileName = loc.sourceName(); 1435 1436 ReferenceType refType = loc.declaringType(); 1437 int lineno = loc.lineNumber(); 1438 1439 if (t.hasMoreTokens()) { 1440 String id = t.nextToken(); 1441 1442 // See if token is a line number. 1443 try { 1444 NumberFormat nf = NumberFormat.getNumberInstance(); 1445 nf.setParseIntegerOnly(true); 1446 Number n = nf.parse(id); 1447 lineno = n.intValue(); 1448 } catch (java.text.ParseException jtpe) { 1449 // It isn't -- see if it's a method name. 1450 List<Method> meths = refType.methodsByName(id); 1451 if (meths == null || meths.size() == 0) { 1452 MessageOutput.println("is not a valid line number or method name for", 1453 new Object [] {id, refType.name()}); 1454 return; 1455 } else if (meths.size() > 1) { 1456 MessageOutput.println("is an ambiguous method name in", 1457 new Object [] {id, refType.name()}); 1458 return; 1459 } 1460 loc = meths.get(0).location(); 1461 lineno = loc.lineNumber(); 1462 } 1463 } 1464 int startLine = Math.max(lineno - 4, 1); 1465 int endLine = startLine + 9; 1466 if (lineno < 0) { 1467 MessageOutput.println("Line number information not available for"); 1468 } else if (Env.sourceLine(loc, lineno) == null) { 1469 MessageOutput.println("is an invalid line number for", 1470 new Object [] {new Integer (lineno), 1471 refType.name()}); 1472 } else { 1473 for (int i = startLine; i <= endLine; i++) { 1474 String sourceLine = Env.sourceLine(loc, i); 1475 if (sourceLine == null) { 1476 break; 1477 } 1478 if (i == lineno) { 1479 MessageOutput.println("source line number current line and line", 1480 new Object [] {new Integer (i), 1481 sourceLine}); 1482 } else { 1483 MessageOutput.println("source line number and line", 1484 new Object [] {new Integer (i), 1485 sourceLine}); 1486 } 1487 } 1488 } 1489 } catch (AbsentInformationException e) { 1490 MessageOutput.println("No source information available for:", loc.toString()); 1491 } catch(FileNotFoundException exc) { 1492 MessageOutput.println("Source file not found:", sourceFileName); 1493 } catch(IOException exc) { 1494 MessageOutput.println("I/O exception occurred:", exc.toString()); 1495 } 1496 } 1497 1498 void commandLines(StringTokenizer t) { // Undocumented command: useful for testing 1499 if (!t.hasMoreTokens()) { 1500 MessageOutput.println("Specify class and method"); 1501 } else { 1502 String idClass = t.nextToken(); 1503 String idMethod = t.hasMoreTokens() ? t.nextToken() : null; 1504 try { 1505 ReferenceType refType = Env.getReferenceTypeFromToken(idClass); 1506 if (refType != null) { 1507 List<Location> lines = null; 1508 if (idMethod == null) { 1509 lines = refType.allLineLocations(); 1510 } else { 1511 for (Method method : refType.allMethods()) { 1512 if (method.name().equals(idMethod)) { 1513 lines = method.allLineLocations(); 1514 } 1515 } 1516 if (lines == null) { 1517 MessageOutput.println("is not a valid method name", idMethod); 1518 } 1519 } 1520 for (Location line : lines) { 1521 MessageOutput.printDirectln(line.toString());// Special case: use printDirectln() 1522 } 1523 } else { 1524 MessageOutput.println("is not a valid id or class name", idClass); 1525 } 1526 } catch (AbsentInformationException e) { 1527 MessageOutput.println("Line number information not available for", idClass); 1528 } 1529 } 1530 } 1531 1532 void commandClasspath(StringTokenizer t) { 1533 if (Env.vm() instanceof PathSearchingVirtualMachine) { 1534 PathSearchingVirtualMachine vm = (PathSearchingVirtualMachine)Env.vm(); 1535 MessageOutput.println("base directory:", vm.baseDirectory()); 1536 MessageOutput.println("classpath:", vm.classPath().toString()); 1537 MessageOutput.println("bootclasspath:", vm.bootClassPath().toString()); 1538 } else { 1539 MessageOutput.println("The VM does not use paths"); 1540 } 1541 } 1542 1543 /* Get or set the source file path list. */ 1544 void commandUse(StringTokenizer t) { 1545 if (!t.hasMoreTokens()) { 1546 MessageOutput.printDirectln(Env.getSourcePath());// Special case: use printDirectln() 1547 } else { 1548 /* 1549 * Take the remainder of the command line, minus 1550 * leading or trailing whitespace. Embedded 1551 * whitespace is fine. 1552 */ 1553 Env.setSourcePath(t.nextToken("").trim()); 1554 } 1555 } 1556 1557 /* Print a stack variable */ 1558 private void printVar(LocalVariable var, Value value) { 1559 MessageOutput.println("expr is value", 1560 new Object [] {var.name(), 1561 value == null ? "null" : value.toString()}); 1562 } 1563 1564 /* Print all local variables in current stack frame. */ 1565 void commandLocals() { 1566 StackFrame frame; 1567 ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); 1568 if (threadInfo == null) { 1569 MessageOutput.println("No default thread specified:"); 1570 return; 1571 } 1572 try { 1573 frame = threadInfo.getCurrentFrame(); 1574 if (frame == null) { 1575 throw new AbsentInformationException(); 1576 } 1577 List<LocalVariable> vars = frame.visibleVariables(); 1578 1579 if (vars.size() == 0) { 1580 MessageOutput.println("No local variables"); 1581 return; 1582 } 1583 Map<LocalVariable, Value> values = frame.getValues(vars); 1584 1585 MessageOutput.println("Method arguments:"); 1586 for (LocalVariable var : vars) { 1587 if (var.isArgument()) { 1588 Value val = values.get(var); 1589 printVar(var, val); 1590 } 1591 } 1592 MessageOutput.println("Local variables:"); 1593 for (LocalVariable var : vars) { 1594 if (!var.isArgument()) { 1595 Value val = values.get(var); 1596 printVar(var, val); 1597 } 1598 } 1599 } catch (AbsentInformationException aie) { 1600 MessageOutput.println("Local variable information not available."); 1601 } catch (IncompatibleThreadStateException exc) { 1602 MessageOutput.println("Current thread isnt suspended."); 1603 } 1604 } 1605 1606 private void dump(ObjectReference obj, ReferenceType refType, 1607 ReferenceType refTypeBase) { 1608 for (Field field : refType.fields()) { 1609 StringBuilder sb = new StringBuilder(); 1610 sb.append(" "); 1611 if (!refType.equals(refTypeBase)) { 1612 sb.append(refType.name()); 1613 sb.append("."); 1614 } 1615 sb.append(field.name()); 1616 sb.append(MessageOutput.format("colon space")); 1617 sb.append(obj.getValue(field)); 1618 MessageOutput.printDirectln(sb.toString()); // Special case: use printDirectln() 1619 } 1620 if (refType instanceof ClassType) { 1621 ClassType sup = ((ClassType)refType).superclass(); 1622 if (sup != null) { 1623 dump(obj, sup, refTypeBase); 1624 } 1625 } else if (refType instanceof InterfaceType) { 1626 for (InterfaceType sup : ((InterfaceType)refType).superinterfaces()) { 1627 dump(obj, sup, refTypeBase); 1628 } 1629 } else { 1630 /* else refType is an instanceof ArrayType */ 1631 if (obj instanceof ArrayReference) { 1632 for (Iterator<Value> it = ((ArrayReference)obj).getValues().iterator(); 1633 it.hasNext(); ) { 1634 MessageOutput.printDirect(it.next().toString());// Special case: use printDirect() 1635 if (it.hasNext()) { 1636 MessageOutput.printDirect(", ");// Special case: use printDirect() 1637 } 1638 } 1639 MessageOutput.println(); 1640 } 1641 } 1642 } 1643 1644 /* Print a specified reference. 1645 */ 1646 void doPrint(StringTokenizer t, boolean dumpObject) { 1647 if (!t.hasMoreTokens()) { 1648 MessageOutput.println("No objects specified."); 1649 return; 1650 } 1651 1652 while (t.hasMoreTokens()) { 1653 String expr = t.nextToken(""); 1654 Value val = evaluate(expr); 1655 if (val == null) { 1656 MessageOutput.println("expr is null", expr.toString()); 1657 } else if (dumpObject && (val instanceof ObjectReference) && 1658 !(val instanceof StringReference)) { 1659 ObjectReference obj = (ObjectReference)val; 1660 ReferenceType refType = obj.referenceType(); 1661 MessageOutput.println("expr is value", 1662 new Object [] {expr.toString(), 1663 MessageOutput.format("grouping begin character")}); 1664 dump(obj, refType, refType); 1665 MessageOutput.println("grouping end character"); 1666 } else { 1667 String strVal = getStringValue(); 1668 if (strVal != null) { 1669 MessageOutput.println("expr is value", new Object [] {expr.toString(), 1670 strVal}); 1671 } 1672 } 1673 } 1674 } 1675 1676 void commandPrint(final StringTokenizer t, final boolean dumpObject) { 1677 new AsyncExecution() { 1678 @Override 1679 void action() { 1680 doPrint(t, dumpObject); 1681 } 1682 }; 1683 } 1684 1685 void commandSet(final StringTokenizer t) { 1686 String all = t.nextToken(""); 1687 1688 /* 1689 * Bare bones error checking. 1690 */ 1691 if (all.indexOf('=') == -1) { 1692 MessageOutput.println("Invalid assignment syntax"); 1693 MessageOutput.printPrompt(); 1694 return; 1695 } 1696 1697 /* 1698 * The set command is really just syntactic sugar. Pass it on to the 1699 * print command. 1700 */ 1701 commandPrint(new StringTokenizer(all), false); 1702 } 1703 1704 void doLock(StringTokenizer t) { 1705 if (!t.hasMoreTokens()) { 1706 MessageOutput.println("No object specified."); 1707 return; 1708 } 1709 1710 String expr = t.nextToken(""); 1711 Value val = evaluate(expr); 1712 1713 try { 1714 if ((val != null) && (val instanceof ObjectReference)) { 1715 ObjectReference object = (ObjectReference)val; 1716 String strVal = getStringValue(); 1717 if (strVal != null) { 1718 MessageOutput.println("Monitor information for expr", 1719 new Object [] {expr.trim(), 1720 strVal}); 1721 } 1722 ThreadReference owner = object.owningThread(); 1723 if (owner == null) { 1724 MessageOutput.println("Not owned"); 1725 } else { 1726 MessageOutput.println("Owned by:", 1727 new Object [] {owner.name(), 1728 new Integer (object.entryCount())}); 1729 } 1730 List<ThreadReference> waiters = object.waitingThreads(); 1731 if (waiters.size() == 0) { 1732 MessageOutput.println("No waiters"); 1733 } else { 1734 for (ThreadReference waiter : waiters) { 1735 MessageOutput.println("Waiting thread:", waiter.name()); 1736 } 1737 } 1738 } else { 1739 MessageOutput.println("Expression must evaluate to an object"); 1740 } 1741 } catch (IncompatibleThreadStateException e) { 1742 MessageOutput.println("Threads must be suspended"); 1743 } 1744 } 1745 1746 void commandLock(final StringTokenizer t) { 1747 new AsyncExecution() { 1748 @Override 1749 void action() { 1750 doLock(t); 1751 } 1752 }; 1753 } 1754 1755 private void printThreadLockInfo(ThreadInfo threadInfo) { 1756 ThreadReference thread = threadInfo.getThread(); 1757 try { 1758 MessageOutput.println("Monitor information for thread", thread.name()); 1759 List<ObjectReference> owned = thread.ownedMonitors(); 1760 if (owned.size() == 0) { 1761 MessageOutput.println("No monitors owned"); 1762 } else { 1763 for (ObjectReference monitor : owned) { 1764 MessageOutput.println("Owned monitor:", monitor.toString()); 1765 } 1766 } 1767 ObjectReference waiting = thread.currentContendedMonitor(); 1768 if (waiting == null) { 1769 MessageOutput.println("Not waiting for a monitor"); 1770 } else { 1771 MessageOutput.println("Waiting for monitor:", waiting.toString()); 1772 } 1773 } catch (IncompatibleThreadStateException e) { 1774 MessageOutput.println("Threads must be suspended"); 1775 } 1776 } 1777 1778 void commandThreadlocks(final StringTokenizer t) { 1779 if (!t.hasMoreTokens()) { 1780 ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); 1781 if (threadInfo == null) { 1782 MessageOutput.println("Current thread not set."); 1783 } else { 1784 printThreadLockInfo(threadInfo); 1785 } 1786 return; 1787 } 1788 String token = t.nextToken(); 1789 if (token.toLowerCase().equals("all")) { 1790 for (ThreadInfo threadInfo : ThreadInfo.threads()) { 1791 printThreadLockInfo(threadInfo); 1792 } 1793 } else { 1794 ThreadInfo threadInfo = doGetThread(token); 1795 if (threadInfo != null) { 1796 ThreadInfo.setCurrentThreadInfo(threadInfo); 1797 printThreadLockInfo(threadInfo); 1798 } 1799 } 1800 } 1801 1802 void doDisableGC(StringTokenizer t) { 1803 if (!t.hasMoreTokens()) { 1804 MessageOutput.println("No object specified."); 1805 return; 1806 } 1807 1808 String expr = t.nextToken(""); 1809 Value val = evaluate(expr); 1810 if ((val != null) && (val instanceof ObjectReference)) { 1811 ObjectReference object = (ObjectReference)val; 1812 object.disableCollection(); 1813 String strVal = getStringValue(); 1814 if (strVal != null) { 1815 MessageOutput.println("GC Disabled for", strVal); 1816 } 1817 } else { 1818 MessageOutput.println("Expression must evaluate to an object"); 1819 } 1820 } 1821 1822 void commandDisableGC(final StringTokenizer t) { 1823 new AsyncExecution() { 1824 @Override 1825 void action() { 1826 doDisableGC(t); 1827 } 1828 }; 1829 } 1830 1831 void doEnableGC(StringTokenizer t) { 1832 if (!t.hasMoreTokens()) { 1833 MessageOutput.println("No object specified."); 1834 return; 1835 } 1836 1837 String expr = t.nextToken(""); 1838 Value val = evaluate(expr); 1839 if ((val != null) && (val instanceof ObjectReference)) { 1840 ObjectReference object = (ObjectReference)val; 1841 object.enableCollection(); 1842 String strVal = getStringValue(); 1843 if (strVal != null) { 1844 MessageOutput.println("GC Enabled for", strVal); 1845 } 1846 } else { 1847 MessageOutput.println("Expression must evaluate to an object"); 1848 } 1849 } 1850 1851 void commandEnableGC(final StringTokenizer t) { 1852 new AsyncExecution() { 1853 @Override 1854 void action() { 1855 doEnableGC(t); 1856 } 1857 }; 1858 } 1859 1860 void doSave(StringTokenizer t) {// Undocumented command: useful for testing. 1861 if (!t.hasMoreTokens()) { 1862 MessageOutput.println("No save index specified."); 1863 return; 1864 } 1865 1866 String key = t.nextToken(); 1867 1868 if (!t.hasMoreTokens()) { 1869 MessageOutput.println("No expression specified."); 1870 return; 1871 } 1872 String expr = t.nextToken(""); 1873 Value val = evaluate(expr); 1874 if (val != null) { 1875 Env.setSavedValue(key, val); 1876 String strVal = getStringValue(); 1877 if (strVal != null) { 1878 MessageOutput.println("saved", strVal); 1879 } 1880 } else { 1881 MessageOutput.println("Expression cannot be void"); 1882 } 1883 } 1884 1885 void commandSave(final StringTokenizer t) { // Undocumented command: useful for testing. 1886 if (!t.hasMoreTokens()) { 1887 Set<String> keys = Env.getSaveKeys(); 1888 if (keys.isEmpty()) { 1889 MessageOutput.println("No saved values"); 1890 return; 1891 } 1892 for (String key : keys) { 1893 Value value = Env.getSavedValue(key); 1894 if ((value instanceof ObjectReference) && 1895 ((ObjectReference)value).isCollected()) { 1896 MessageOutput.println("expr is value <collected>", 1897 new Object [] {key, value.toString()}); 1898 } else { 1899 if (value == null){ 1900 MessageOutput.println("expr is null", key); 1901 } else { 1902 MessageOutput.println("expr is value", 1903 new Object [] {key, value.toString()}); 1904 } 1905 } 1906 } 1907 } else { 1908 new AsyncExecution() { 1909 @Override 1910 void action() { 1911 doSave(t); 1912 } 1913 }; 1914 } 1915 1916 } 1917 1918 void commandBytecodes(final StringTokenizer t) { // Undocumented command: useful for testing. 1919 if (!t.hasMoreTokens()) { 1920 MessageOutput.println("No class specified."); 1921 return; 1922 } 1923 String className = t.nextToken(); 1924 1925 if (!t.hasMoreTokens()) { 1926 MessageOutput.println("No method specified."); 1927 return; 1928 } 1929 // Overloading is not handled here. 1930 String methodName = t.nextToken(); 1931 1932 List<ReferenceType> classes = Env.vm().classesByName(className); 1933 // TO DO: handle multiple classes found 1934 if (classes.size() == 0) { 1935 if (className.indexOf('.') < 0) { 1936 MessageOutput.println("not found (try the full name)", className); 1937 } else { 1938 MessageOutput.println("not found", className); 1939 } 1940 return; 1941 } 1942 1943 ReferenceType rt = classes.get(0); 1944 if (!(rt instanceof ClassType)) { 1945 MessageOutput.println("not a class", className); 1946 return; 1947 } 1948 1949 byte[] bytecodes = null; 1950 for (Method method : rt.methodsByName(methodName)) { 1951 if (!method.isAbstract()) { 1952 bytecodes = method.bytecodes(); 1953 break; 1954 } 1955 } 1956 1957 StringBuilder line = new StringBuilder(80); 1958 line.append("0000: "); 1959 for (int i = 0; i < bytecodes.length; i++) { 1960 if ((i > 0) && (i % 16 == 0)) { 1961 MessageOutput.printDirectln(line.toString());// Special case: use printDirectln() 1962 line.setLength(0); 1963 line.append(String.valueOf(i)); 1964 line.append(": "); 1965 int len = line.length(); 1966 for (int j = 0; j < 6 - len; j++) { 1967 line.insert(0, '0'); 1968 } 1969 } 1970 int val = 0xff & bytecodes[i]; 1971 String str = Integer.toHexString(val); 1972 if (str.length() == 1) { 1973 line.append('0'); 1974 } 1975 line.append(str); 1976 line.append(' '); 1977 } 1978 if (line.length() > 6) { 1979 MessageOutput.printDirectln(line.toString());// Special case: use printDirectln() 1980 } 1981 } 1982 1983 void commandExclude(StringTokenizer t) { 1984 if (!t.hasMoreTokens()) { 1985 MessageOutput.printDirectln(Env.excludesString());// Special case: use printDirectln() 1986 } else { 1987 String rest = t.nextToken(""); 1988 if (rest.equals("none")) { 1989 rest = ""; 1990 } 1991 Env.setExcludes(rest); 1992 } 1993 } 1994 1995 void commandRedefine(StringTokenizer t) { 1996 if (!t.hasMoreTokens()) { 1997 MessageOutput.println("Specify classes to redefine"); 1998 } else { 1999 String className = t.nextToken(); 2000 List<ReferenceType> classes = Env.vm().classesByName(className); 2001 if (classes.size() == 0) { 2002 MessageOutput.println("No class named", className); 2003 return; 2004 } 2005 if (classes.size() > 1) { 2006 MessageOutput.println("More than one class named", className); 2007 return; 2008 } 2009 Env.setSourcePath(Env.getSourcePath()); 2010 ReferenceType refType = classes.get(0); 2011 if (!t.hasMoreTokens()) { 2012 MessageOutput.println("Specify file name for class", className); 2013 return; 2014 } 2015 String fileName = t.nextToken(); 2016 File phyl = new File(fileName); 2017 byte[] bytes = new byte[(int)phyl.length()]; 2018 try { 2019 InputStream in = new FileInputStream(phyl); 2020 in.read(bytes); 2021 in.close(); 2022 } catch (Exception exc) { 2023 MessageOutput.println("Error reading file", 2024 new Object [] {fileName, exc.toString()}); 2025 return; 2026 } 2027 Map<ReferenceType, byte[]> map 2028 = new HashMap<ReferenceType, byte[]>(); 2029 map.put(refType, bytes); 2030 try { 2031 Env.vm().redefineClasses(map); 2032 } catch (Throwable exc) { 2033 MessageOutput.println("Error redefining class to file", 2034 new Object [] {className, 2035 fileName, 2036 exc}); 2037 } 2038 } 2039 } 2040 2041 void commandPopFrames(StringTokenizer t, boolean reenter) { 2042 ThreadInfo threadInfo; 2043 2044 if (t.hasMoreTokens()) { 2045 String token = t.nextToken(); 2046 threadInfo = doGetThread(token); 2047 if (threadInfo == null) { 2048 return; 2049 } 2050 } else { 2051 threadInfo = ThreadInfo.getCurrentThreadInfo(); 2052 if (threadInfo == null) { 2053 MessageOutput.println("No thread specified."); 2054 return; 2055 } 2056 } 2057 2058 try { 2059 StackFrame frame = threadInfo.getCurrentFrame(); 2060 threadInfo.getThread().popFrames(frame); 2061 threadInfo = ThreadInfo.getCurrentThreadInfo(); 2062 ThreadInfo.setCurrentThreadInfo(threadInfo); 2063 if (reenter) { 2064 commandStepi(); 2065 } 2066 } catch (Throwable exc) { 2067 MessageOutput.println("Error popping frame", exc.toString()); 2068 } 2069 } 2070 2071 void commandExtension(StringTokenizer t) { 2072 if (!t.hasMoreTokens()) { 2073 MessageOutput.println("No class specified."); 2074 return; 2075 } 2076 2077 String idClass = t.nextToken(); 2078 ReferenceType cls = Env.getReferenceTypeFromToken(idClass); 2079 String extension = null; 2080 if (cls != null) { 2081 try { 2082 extension = cls.sourceDebugExtension(); 2083 MessageOutput.println("sourcedebugextension", extension); 2084 } catch (AbsentInformationException e) { 2085 MessageOutput.println("No sourcedebugextension specified"); 2086 } 2087 } else { 2088 MessageOutput.println("is not a valid id or class name", idClass); 2089 } 2090 } 2091 2092 void commandVersion(String debuggerName, 2093 VirtualMachineManager vmm) { 2094 MessageOutput.println("minus version", 2095 new Object [] { debuggerName, 2096 new Integer(vmm.majorInterfaceVersion()), 2097 new Integer(vmm.minorInterfaceVersion()), 2098 System.getProperty("java.version")}); 2099 if (Env.connection() != null) { 2100 try { 2101 MessageOutput.printDirectln(Env.vm().description());// Special case: use printDirectln() 2102 } catch (VMNotConnectedException e) { 2103 MessageOutput.println("No VM connected"); 2104 } 2105 } 2106 } 2107 }