History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/com/sun/tools/example/debug/gui/CommandInterpreter.java (1 of 18352 files). Show all

Unified · Split

java/pkg/com/sun/tools/example/debug/gui/CommandInterpreter.java+1459−0
@@ -0,0 +1,1459 @@
1/*
2 * Copyright (c) 1998, 2008, 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
26package com.sun.tools.example.debug.gui;
27
28import java.io.*;
29import java.util.*;
30
31import com.sun.jdi.*;
32import com.sun.tools.example.debug.bdi.*;
33
34public class CommandInterpreter {
35
36 boolean echo;
37
38 Environment env;
39
40 private ContextManager context;
41 private ExecutionManager runtime;
42 private ClassManager classManager;
43 private SourceManager sourceManager;
44
45 private OutputSink out; //### Hack! Should be local in each method used.
46 private String lastCommand = "help";
47
48 public CommandInterpreter(Environment env) {
49 this(env, true);
50 }
51
52 public CommandInterpreter(Environment env, boolean echo) {
53 this.env = env;
54 this.echo = echo;
55 this.runtime = env.getExecutionManager();
56 this.context = env.getContextManager();
57 this.classManager = env.getClassManager();
58 this.sourceManager = env.getSourceManager();
59 }
60
61 private ThreadReference[] threads = null;
62
63 /*
64 * The numbering of threads is relative to the current set of threads,
65 * and may be affected by the creation and termination of new threads.
66 * Commands issued using such thread ids will only give reliable behavior
67 * relative to what was shown earlier in 'list' commands if the VM is interrupted.
68 * We need a better scheme.
69 */
70
71 private ThreadReference[] threads() throws NoSessionException {
72 if (threads == null) {
73 ThreadIterator ti = new ThreadIterator(getDefaultThreadGroup());
74 List<ThreadReference> tlist = new ArrayList<ThreadReference>();
75 while (ti.hasNext()) {
76 tlist.add(ti.nextThread());
77 }
78 threads = tlist.toArray(new ThreadReference[tlist.size()]);
79 }
80 return threads;
81 }
82
83 private ThreadReference findThread(String idToken) throws NoSessionException {
84 String id;
85 ThreadReference thread = null;
86 if (idToken.startsWith("t@")) {
87 id = idToken.substring(2);
88 } else {
89 id = idToken;
90 }
91 try {
92 ThreadReference[] threads = threads();
93 long threadID = Long.parseLong(id, 16);
94 for (ThreadReference thread2 : threads) {
95 if (thread2.uniqueID() == threadID) {
96 thread = thread2;
97 break;
98 }
99 }
100 if (thread == null) {
101 //env.failure("No thread for id \"" + idToken + "\"");
102 env.failure("\"" + idToken + "\" is not a valid thread id.");
103 }
104 } catch (NumberFormatException e) {
105 env.error("Thread id \"" + idToken + "\" is ill-formed.");
106 thread = null;
107 }
108 return thread;
109 }
110
111 private ThreadIterator allThreads() throws NoSessionException {
112 threads = null;
113 //### Why not use runtime.allThreads().iterator() ?
114 return new ThreadIterator(runtime.topLevelThreadGroups());
115 }
116
117 private ThreadIterator currentThreadGroupThreads() throws NoSessionException {
118 threads = null;
119 return new ThreadIterator(getDefaultThreadGroup());
120 }
121
122 private ThreadGroupIterator allThreadGroups() throws NoSessionException {
123 threads = null;
124 return new ThreadGroupIterator(runtime.topLevelThreadGroups());
125 }
126
127 private ThreadGroupReference defaultThreadGroup;
128
129 private ThreadGroupReference getDefaultThreadGroup() throws NoSessionException {
130 if (defaultThreadGroup == null) {
131 defaultThreadGroup = runtime.systemThreadGroup();
132 }
133 return defaultThreadGroup;
134 }
135
136 private void setDefaultThreadGroup(ThreadGroupReference tg) {
137 defaultThreadGroup = tg;
138 }
139
140 /*
141 * Command handlers.
142 */
143
144 // Command: classes
145
146 private void commandClasses() throws NoSessionException {
147 OutputSink out = env.getOutputSink();
148 //out.println("** classes list **");
149 for (ReferenceType refType : runtime.allClasses()) {
150 out.println(refType.name());
151 }
152 out.show();
153 }
154
155
156 // Command: methods
157
158 private void commandMethods(StringTokenizer t) throws NoSessionException {
159 if (!t.hasMoreTokens()) {
160 env.error("No class specified.");
161 return;
162 }
163 String idClass = t.nextToken();
164 ReferenceType cls = findClass(idClass);
165 if (cls != null) {
166 List<Method> methods = cls.allMethods();
167 OutputSink out = env.getOutputSink();
168 for (int i = 0; i < methods.size(); i++) {
169 Method method = methods.get(i);
170 out.print(method.declaringType().name() + " " +
171 method.name() + "(");
172 Iterator<String> it = method.argumentTypeNames().iterator();
173 if (it.hasNext()) {
174 while (true) {
175 out.print(it.next());
176 if (!it.hasNext()) {
177 break;
178 }
179 out.print(", ");
180 }
181 }
182 out.println(")");
183 }
184 out.show();
185 } else {
186 //### Should validate class name syntax.
187 env.failure("\"" + idClass + "\" is not a valid id or class name.");
188 }
189 }
190
191 private ReferenceType findClass(String pattern) throws NoSessionException {
192 List<ReferenceType> results = runtime.findClassesMatchingPattern(pattern);
193 if (results.size() > 0) {
194 //### Should handle multiple results sensibly.
195 return results.get(0);
196 }
197 return null;
198 }
199
200 // Command: threads
201
202 private void commandThreads(StringTokenizer t) throws NoSessionException {
203 if (!t.hasMoreTokens()) {
204 OutputSink out = env.getOutputSink();
205 printThreadGroup(out, getDefaultThreadGroup(), 0);
206 out.show();
207 return;
208 }
209 String name = t.nextToken();
210 ThreadGroupReference tg = findThreadGroup(name);
211 if (tg == null) {
212 env.failure(name + " is not a valid threadgroup name.");
213 } else {
214 OutputSink out = env.getOutputSink();
215 printThreadGroup(out, tg, 0);
216 out.show();
217 }
218 }
219
220 private ThreadGroupReference findThreadGroup(String name) throws NoSessionException {
221 //### Issue: Uniqueness of thread group names is not enforced.
222 ThreadGroupIterator tgi = allThreadGroups();
223 while (tgi.hasNext()) {
224 ThreadGroupReference tg = tgi.nextThreadGroup();
225 if (tg.name().equals(name)) {
226 return tg;
227 }
228 }
229 return null;
230 }
231
232 private int printThreadGroup(OutputSink out, ThreadGroupReference tg, int iThread) {
233 out.println("Group " + tg.name() + ":");
234 List<ThreadReference> tlist = tg.threads();
235 int maxId = 0;
236 int maxName = 0;
237 for (int i = 0 ; i < tlist.size() ; i++) {
238 ThreadReference thr = tlist.get(i);
239 int len = Utils.description(thr).length();
240 if (len > maxId) {
241 maxId = len;
242 }
243 String name = thr.name();
244 int iDot = name.lastIndexOf('.');
245 if (iDot >= 0 && name.length() > iDot) {
246 name = name.substring(iDot + 1);
247 }
248 if (name.length() > maxName) {
249 maxName = name.length();
250 }
251 }
252 String maxNumString = String.valueOf(iThread + tlist.size());
253 int maxNumDigits = maxNumString.length();
254 for (int i = 0 ; i < tlist.size() ; i++) {
255 ThreadReference thr = tlist.get(i);
256 char buf[] = new char[80];
257 for (int j = 0; j < 79; j++) {
258 buf[j] = ' ';
259 }
260 buf[79] = '\0';
261 StringBuffer sbOut = new StringBuffer();
262 sbOut.append(buf);
263
264 // Right-justify the thread number at start of output string
265 String numString = String.valueOf(iThread + i + 1);
266 sbOut.insert(maxNumDigits - numString.length(),
267 numString);
268 sbOut.insert(maxNumDigits, ".");
269
270 int iBuf = maxNumDigits + 2;
271 sbOut.insert(iBuf, Utils.description(thr));
272 iBuf += maxId + 1;
273 String name = thr.name();
274 int iDot = name.lastIndexOf('.');
275 if (iDot >= 0 && name.length() > iDot) {
276 name = name.substring(iDot + 1);
277 }
278 sbOut.insert(iBuf, name);
279 iBuf += maxName + 1;
280 sbOut.insert(iBuf, Utils.getStatus(thr));
281 sbOut.setLength(79);
282 out.println(sbOut.toString());
283 }
284 for (ThreadGroupReference tg0 : tg.threadGroups()) {
285 if (!tg.equals(tg0)) { // TODO ref mgt
286 iThread += printThreadGroup(out, tg0, iThread + tlist.size());
287 }
288 }
289 return tlist.size();
290 }
291
292 // Command: threadgroups
293
294 private void commandThreadGroups() throws NoSessionException {
295 ThreadGroupIterator it = allThreadGroups();
296 int cnt = 0;
297 OutputSink out = env.getOutputSink();
298 while (it.hasNext()) {
299 ThreadGroupReference tg = it.nextThreadGroup();
300 ++cnt;
301 out.println("" + cnt + ". " + Utils.description(tg) + " " + tg.name());
302 }
303 out.show();
304 }
305
306 // Command: thread
307
308 private void commandThread(StringTokenizer t) throws NoSessionException {
309 if (!t.hasMoreTokens()) {
310 env.error("Thread number not specified.");
311 return;
312 }
313 ThreadReference thread = findThread(t.nextToken());
314 if (thread != null) {
315 //### Should notify user.
316 context.setCurrentThread(thread);
317 }
318 }
319
320 // Command: threadgroup
321
322 private void commandThreadGroup(StringTokenizer t) throws NoSessionException {
323 if (!t.hasMoreTokens()) {
324 env.error("Threadgroup name not specified.");
325 return;
326 }
327 String name = t.nextToken();
328 ThreadGroupReference tg = findThreadGroup(name);
329 if (tg == null) {
330 env.failure(name + " is not a valid threadgroup name.");
331 } else {
332 //### Should notify user.
333 setDefaultThreadGroup(tg);
334 }
335 }
336
337 // Command: run
338
339 private void commandRun(StringTokenizer t) throws NoSessionException {
340 if (doLoad(false, t)) {
341 env.notice("Running ...");
342 }
343 }
344
345 // Command: load
346
347 private void commandLoad(StringTokenizer t) throws NoSessionException {
348 if (doLoad(true, t)) {}
349 }
350
351 private boolean doLoad(boolean suspended,
352 StringTokenizer t) throws NoSessionException {
353
354 String clname;
355
356 if (!t.hasMoreTokens()) {
357 clname = context.getMainClassName();
358 if (!clname.equals("")) {
359 // Run from prevously-set class name.
360 try {
361 String vmArgs = context.getVmArguments();
362 runtime.run(suspended,
363 vmArgs,
364 clname,
365 context.getProgramArguments());
366 return true;
367 } catch (VMLaunchFailureException e) {
368 env.failure("Attempt to launch main class \"" + clname + "\" failed.");
369 }
370 } else {
371 env.failure("No main class specifed and no current default defined.");
372 }
373 } else {
374 clname = t.nextToken();
375 StringBuffer sbuf = new StringBuffer();
376 // Allow VM arguments to be specified here?
377 while (t.hasMoreTokens()) {
378 String tok = t.nextToken();
379 sbuf.append(tok);
380 if (t.hasMoreTokens()) {
381 sbuf.append(' ');
382 }
383 }
384 String args = sbuf.toString();
385 try {
386 String vmArgs = context.getVmArguments();
387 runtime.run(suspended, vmArgs, clname, args);
388 context.setMainClassName(clname);
389 //context.setVmArguments(vmArgs);
390 context.setProgramArguments(args);
391 return true;
392 } catch (VMLaunchFailureException e) {
393 env.failure("Attempt to launch main class \"" + clname + "\" failed.");
394 }
395 }
396 return false;
397 }
398
399 // Command: connect
400
401 private void commandConnect(StringTokenizer t) {
402 try {
403 LaunchTool.queryAndLaunchVM(runtime);
404 } catch (VMLaunchFailureException e) {
405 env.failure("Attempt to connect failed.");
406 }
407 }
408
409 // Command: attach
410
411 private void commandAttach(StringTokenizer t) {
412 String portName;
413 if (!t.hasMoreTokens()) {
414 portName = context.getRemotePort();
415 if (!portName.equals("")) {
416 try {
417 runtime.attach(portName);
418 } catch (VMLaunchFailureException e) {
419 env.failure("Attempt to attach to port \"" + portName + "\" failed.");
420 }
421 } else {
422 env.failure("No port specifed and no current default defined.");
423 }
424 } else {
425 portName = t.nextToken();
426 try {
427 runtime.attach(portName);
428 } catch (VMLaunchFailureException e) {
429 env.failure("Attempt to attach to port \"" + portName + "\" failed.");
430 }
431 context.setRemotePort(portName);
432 }
433 }
434
435 // Command: detach
436
437 private void commandDetach(StringTokenizer t) throws NoSessionException {
438 runtime.detach();
439 }
440
441 // Command: interrupt
442
443 private void commandInterrupt(StringTokenizer t) throws NoSessionException {
444 runtime.interrupt();
445 }
446
447 // Command: suspend
448
449 private void commandSuspend(StringTokenizer t) throws NoSessionException {
450 if (!t.hasMoreTokens()) {
451 // Suspend all threads in the current thread group.
452 //### Issue: help message says default is all threads.
453 //### Behavior here agrees with 'jdb', however.
454 ThreadIterator ti = currentThreadGroupThreads();
455 while (ti.hasNext()) {
456 // TODO - don't suspend debugger threads
457 ti.nextThread().suspend();
458 }
459 env.notice("All (non-system) threads suspended.");
460 } else {
461 while (t.hasMoreTokens()) {
462 ThreadReference thread = findThread(t.nextToken());
463 if (thread != null) {
464 //thread.suspend();
465 runtime.suspendThread(thread);
466 }
467 }
468 }
469 }
470
471 // Command: resume
472
473 private void commandResume(StringTokenizer t) throws NoSessionException {
474 if (!t.hasMoreTokens()) {
475 // Suspend all threads in the current thread group.
476 //### Issue: help message says default is all threads.
477 //### Behavior here agrees with 'jdb', however.
478 ThreadIterator ti = currentThreadGroupThreads();
479 while (ti.hasNext()) {
480 // TODO - don't suspend debugger threads
481 ti.nextThread().resume();
482 }
483 env.notice("All threads resumed.");
484 } else {
485 while (t.hasMoreTokens()) {
486 ThreadReference thread = findThread(t.nextToken());
487 if (thread != null) {
488 //thread.resume();
489 runtime.resumeThread(thread);
490 }
491 }
492 }
493 }
494
495 // Command: cont
496
497 private void commandCont() throws NoSessionException {
498 try {
499 runtime.go();
500 } catch (VMNotInterruptedException e) {
501 //### failure?
502 env.notice("Target VM is already running.");
503 }
504 }
505
506 // Command: step
507
508 private void commandStep(StringTokenizer t) throws NoSessionException{
509 ThreadReference current = context.getCurrentThread();
510 if (current == null) {
511 env.failure("No current thread.");
512 return;
513 }
514 try {
515 if (t.hasMoreTokens() &&
516 t.nextToken().toLowerCase().equals("up")) {
517 runtime.stepOut(current);
518 } else {
519 runtime.stepIntoLine(current);
520 }
521 } catch (AbsentInformationException e) {
522 env.failure("No linenumber information available -- " +
523 "Try \"stepi\" to step by instructions.");
524 }
525 }
526
527 // Command: stepi
528
529 private void commandStepi() throws NoSessionException {
530 ThreadReference current = context.getCurrentThread();
531 if (current == null) {
532 env.failure("No current thread.");
533 return;
534 }
535 runtime.stepIntoInstruction(current);
536 }
537
538 // Command: next
539
540 private void commandNext() throws NoSessionException {
541 ThreadReference current = context.getCurrentThread();
542 if (current == null) {
543 env.failure("No current thread.");
544 return;
545 }
546 try {
547 runtime.stepOverLine(current);
548 } catch (AbsentInformationException e) {
549 env.failure("No linenumber information available -- " +
550 "Try \"nexti\" to step by instructions.");
551 }
552 }
553
554 // Command: nexti (NEW)
555
556 private void commandNexti() throws NoSessionException {
557 ThreadReference current = context.getCurrentThread();
558 if (current == null) {
559 env.failure("No current thread.");
560 return;
561 }
562 runtime.stepOverInstruction(current);
563 }
564
565 // Command: kill
566
567 private void commandKill(StringTokenizer t) throws NoSessionException {
568 //### Should change the way in which thread ids and threadgroup names
569 //### are distinguished.
570 if (!t.hasMoreTokens()) {
571 env.error("Usage: kill <threadgroup name> or <thread id>");
572 return;
573 }
574 while (t.hasMoreTokens()) {
575 String idToken = t.nextToken();
576 ThreadReference thread = findThread(idToken);
577 if (thread != null) {
578 runtime.stopThread(thread);
579 env.notice("Thread " + thread.name() + " killed.");
580 return;
581 } else {
582 /* Check for threadgroup name, NOT skipping "system". */
583 //### Should skip "system"? Classic 'jdb' does this.
584 //### Should deal with possible non-uniqueness of threadgroup names.
585 ThreadGroupIterator itg = allThreadGroups();
586 while (itg.hasNext()) {
587 ThreadGroupReference tg = itg.nextThreadGroup();
588 if (tg.name().equals(idToken)) {
589 ThreadIterator it = new ThreadIterator(tg);
590 while (it.hasNext()) {
591 runtime.stopThread(it.nextThread());
592 }
593 env.notice("Threadgroup " + tg.name() + "killed.");
594 return;
595 }
596 }
597 env.failure("\"" + idToken +
598 "\" is not a valid threadgroup or id.");
599 }
600 }
601 }
602
603
604 /*************
605 // TODO
606 private void commandCatchException(StringTokenizer t) throws NoSessionException {}
607 // TODO
608 private void commandIgnoreException(StringTokenizer t) throws NoSessionException {}
609 *************/
610
611 // Command: up
612
613 //### Print current frame after command?
614
615 int readCount(StringTokenizer t) {
616 int cnt = 1;
617 if (t.hasMoreTokens()) {
618 String idToken = t.nextToken();
619 try {
620 cnt = Integer.valueOf(idToken).intValue();
621 } catch (NumberFormatException e) {
622 cnt = -1;
623 }
624 }
625 return cnt;
626 }
627
628 void commandUp(StringTokenizer t) throws NoSessionException {
629 ThreadReference current = context.getCurrentThread();
630 if (current == null) {
631 env.failure("No current thread.");
632 return;
633 }
634 int nLevels = readCount(t);
635 if (nLevels <= 0) {
636 env.error("usage: up [n frames]");
637 return;
638 }
639 try {
640 int delta = context.moveCurrentFrameIndex(current, -nLevels);
641 if (delta == 0) {
642 env.notice("Already at top of stack.");
643 } else if (-delta < nLevels) {
644 env.notice("Moved up " + delta + " frames to top of stack.");
645 }
646 } catch (VMNotInterruptedException e) {
647 env.failure("Target VM must be in interrupted state.");
648 }
649 }
650
651 private void commandDown(StringTokenizer t) throws NoSessionException {
652 ThreadReference current = context.getCurrentThread();
653 if (current == null) {
654 env.failure("No current thread.");
655 return;
656 }
657 int nLevels = readCount(t);
658 if (nLevels <= 0) {
659 env.error("usage: down [n frames]");
660 return;
661 }
662 try {
663 int delta = context.moveCurrentFrameIndex(current, nLevels);
664 if (delta == 0) {
665 env.notice("Already at bottom of stack.");
666 } else if (delta < nLevels) {
667 env.notice("Moved down " + delta + " frames to bottom of stack.");
668 }
669 } catch (VMNotInterruptedException e) {
670 env.failure("Target VM must be in interrupted state.");
671 }
672 }
673
674 // Command: frame
675
676 private void commandFrame(StringTokenizer t) throws NoSessionException {
677 ThreadReference current = context.getCurrentThread();
678 if (current == null) {
679 env.failure("No current thread.");
680 return;
681 }
682 if (!t.hasMoreTokens()) {
683 env.error("usage: frame <frame-index>");
684 return;
685 }
686 String idToken = t.nextToken();
687 int n;
688 try {
689 n = Integer.valueOf(idToken).intValue();
690 } catch (NumberFormatException e) {
691 n = 0;
692 }
693 if (n <= 0) {
694 env.error("use positive frame index");
695 return;
696 }
697 try {
698 int delta = context.setCurrentFrameIndex(current, n);
699 if (delta == 0) {
700 env.notice("Frame unchanged.");
701 } else if (delta < 0) {
702 env.notice("Moved up " + -delta + " frames.");
703 } else {
704 env.notice("Moved down " + delta + " frames.");
705 }
706 } catch (VMNotInterruptedException e) {
707 env.failure("Target VM must be in interrupted state.");
708 }
709 }
710
711 // Command: where
712
713 //### Should we insist that VM be interrupted here?
714 //### There is an inconsistency between the 'where' command
715 //### and 'up' and 'down' in this respect.
716
717 private void commandWhere(StringTokenizer t, boolean showPC)
718 throws NoSessionException {
719 ThreadReference current = context.getCurrentThread();
720 if (!t.hasMoreTokens()) {
721 if (current == null) {
722 env.error("No thread specified.");
723 return;
724 }
725 dumpStack(current, showPC);
726 } else {
727 String token = t.nextToken();
728 if (token.toLowerCase().equals("all")) {
729 ThreadIterator it = allThreads();
730 while (it.hasNext()) {
731 ThreadReference thread = it.next();
732 out.println(thread.name() + ": ");
733 dumpStack(thread, showPC);
734 }
735 } else {
736 ThreadReference thread = findThread(t.nextToken());
737 //### Do we want to set current thread here?
738 //### Should notify user of change.
739 if (thread != null) {
740 context.setCurrentThread(thread);
741 }
742 dumpStack(thread, showPC);
743 }
744 }
745 }
746
747 private void dumpStack(ThreadReference thread, boolean showPC) {
748 //### Check for these.
749 //env.failure("Thread no longer exists.");
750 //env.failure("Target VM must be in interrupted state.");
751 //env.failure("Current thread isn't suspended.");
752 //### Should handle extremely long stack traces sensibly for user.
753 List<StackFrame> stack = null;
754 try {
755 stack = thread.frames();
756 } catch (IncompatibleThreadStateException e) {
757 env.failure("Thread is not suspended.");
758 }
759 //### Fix this!
760 //### Previously mishandled cases where thread was not current.
761 //### Now, prints all of the stack regardless of current frame.
762 int frameIndex = 0;
763 //int frameIndex = context.getCurrentFrameIndex();
764 if (stack == null) {
765 env.failure("Thread is not running (no stack).");
766 } else {
767 OutputSink out = env.getOutputSink();
768 int nFrames = stack.size();
769 for (int i = frameIndex; i < nFrames; i++) {
770 StackFrame frame = stack.get(i);
771 Location loc = frame.location();
772 Method meth = loc.method();
773 out.print(" [" + (i + 1) + "] ");
774 out.print(meth.declaringType().name());
775 out.print('.');
776 out.print(meth.name());
777 out.print(" (");
778 if (meth.isNative()) {
779 out.print("native method");
780 } else if (loc.lineNumber() != -1) {
781 try {
782 out.print(loc.sourceName());
783 } catch (AbsentInformationException e) {
784 out.print("<unknown>");
785 }
786 out.print(':');
787 out.print(loc.lineNumber());
788 }
789 out.print(')');
790 if (showPC) {
791 long pc = loc.codeIndex();
792 if (pc != -1) {
793 out.print(", pc = " + pc);
794 }
795 }
796 out.println();
797 }
798 out.show();
799 }
800 }
801
802 private void listEventRequests() throws NoSessionException {
803 // Print set breakpoints
804 List<EventRequestSpec> specs = runtime.eventRequestSpecs();
805 if (specs.isEmpty()) {
806 env.notice("No breakpoints/watchpoints/exceptions set.");
807 } else {
808 OutputSink out = env.getOutputSink();
809 out.println("Current breakpoints/watchpoints/exceptions set:");
810 for (EventRequestSpec bp : specs) {
811 out.println("\t" + bp);
812 }
813 out.show();
814 }
815 }
816
817 private BreakpointSpec parseBreakpointSpec(String bptSpec) {
818 StringTokenizer t = new StringTokenizer(bptSpec);
819 BreakpointSpec bpSpec = null;
820// try {
821 String token = t.nextToken("@:( \t\n\r");
822 // We can't use hasMoreTokens here because it will cause any leading
823 // paren to be lost.
824 String rest;
825 try {
826 rest = t.nextToken("").trim();
827 } catch (NoSuchElementException e) {
828 rest = null;
829 }
830 if ((rest != null) && rest.startsWith("@")) {
831 t = new StringTokenizer(rest.substring(1));
832 String sourceName = token;
833 String lineToken = t.nextToken();
834 int lineNumber = Integer.valueOf(lineToken).intValue();
835 if (t.hasMoreTokens()) {
836 return null;
837 }
838 bpSpec = runtime.createSourceLineBreakpoint(sourceName,
839 lineNumber);
840 } else if ((rest != null) && rest.startsWith(":")) {
841 t = new StringTokenizer(rest.substring(1));
842 String classId = token;
843 String lineToken = t.nextToken();
844 int lineNumber = Integer.valueOf(lineToken).intValue();
845 if (t.hasMoreTokens()) {
846 return null;
847 }
848 bpSpec = runtime.createClassLineBreakpoint(classId, lineNumber);
849 } else {
850 // Try stripping method from class.method token.
851 int idot = token.lastIndexOf(".");
852 if ( (idot <= 0) || /* No dot or dot in first char */
853 (idot >= token.length() - 1) ) { /* dot in last char */
854 return null;
855 }
856 String methodName = token.substring(idot + 1);
857 String classId = token.substring(0, idot);
858 List<String> argumentList = null;
859 if (rest != null) {
860 if (!rest.startsWith("(") || !rest.endsWith(")")) {
861 //### Should throw exception with error message
862 //out.println("Invalid method specification: "
863 // + methodName + rest);
864 return null;
865 }
866 // Trim the parens
867 //### What about spaces in arglist?
868 rest = rest.substring(1, rest.length() - 1);
869 argumentList = new ArrayList<String>();
870 t = new StringTokenizer(rest, ",");
871 while (t.hasMoreTokens()) {
872 argumentList.add(t.nextToken());
873 }
874 }
875 bpSpec = runtime.createMethodBreakpoint(classId,
876 methodName,
877 argumentList);
878 }
879// } catch (Exception e) {
880// env.error("Exception attempting to create breakpoint: " + e);
881// return null;
882// }
883 return bpSpec;
884 }
885
886 private void commandStop(StringTokenizer t) throws NoSessionException {
887 String token;
888
889 if (!t.hasMoreTokens()) {
890 listEventRequests();
891 } else {
892 token = t.nextToken();
893 // Ignore optional "at" or "in" token.
894 // Allowed for backward compatibility.
895 if (token.equals("at") || token.equals("in")) {
896 if (t.hasMoreTokens()) {
897 token = t.nextToken();
898 } else {
899 env.error("Missing breakpoint specification.");
900 return;
901 }
902 }
903 BreakpointSpec bpSpec = parseBreakpointSpec(token);
904 if (bpSpec != null) {
905 //### Add sanity-checks for deferred breakpoint.
906 runtime.install(bpSpec);
907 } else {
908 env.error("Ill-formed breakpoint specification.");
909 }
910 }
911 }
912
913 private void commandClear(StringTokenizer t) throws NoSessionException {
914 if (!t.hasMoreTokens()) {
915 // Print set breakpoints
916 listEventRequests();
917 return;
918 }
919 //### need 'clear all'
920 BreakpointSpec bpSpec = parseBreakpointSpec(t.nextToken());
921 if (bpSpec != null) {
922 List<EventRequestSpec> specs = runtime.eventRequestSpecs();
923
924 if (specs.isEmpty()) {
925 env.notice("No breakpoints set.");
926 } else {
927 List<EventRequestSpec> toDelete = new ArrayList<EventRequestSpec>();
928 for (EventRequestSpec spec : specs) {
929 if (spec.equals(bpSpec)) {
930 toDelete.add(spec);
931 }
932 }
933 // The request used for matching should be found
934 if (toDelete.size() <= 1) {
935 env.notice("No matching breakpoint set.");
936 }
937 for (EventRequestSpec spec : toDelete) {
938 runtime.delete(spec);
939 }
940 }
941 } else {
942 env.error("Ill-formed breakpoint specification.");
943 }
944 }
945
946 // Command: list
947
948 private void commandList(StringTokenizer t) throws NoSessionException {
949 ThreadReference current = context.getCurrentThread();
950 if (current == null) {
951 env.error("No thread specified.");
952 return;
953 }
954 Location loc;
955 try {
956 StackFrame frame = context.getCurrentFrame(current);
957 if (frame == null) {
958 env.failure("Thread has not yet begun execution.");
959 return;
960 }
961 loc = frame.location();
962 } catch (VMNotInterruptedException e) {
963 env.failure("Target VM must be in interrupted state.");
964 return;
965 }
966 SourceModel source = sourceManager.sourceForLocation(loc);
967 if (source == null) {
968 if (loc.method().isNative()) {
969 env.failure("Current method is native.");
970 return;
971 }
972 env.failure("No source available for " + Utils.locationString(loc) + ".");
973 return;
974 }
975 ReferenceType refType = loc.declaringType();
976 int lineno = loc.lineNumber();
977 if (t.hasMoreTokens()) {
978 String id = t.nextToken();
979 // See if token is a line number.
980 try {
981 lineno = Integer.valueOf(id).intValue();
982 } catch (NumberFormatException nfe) {
983 // It isn't -- see if it's a method name.
984 List<Method> meths = refType.methodsByName(id);
985 if (meths == null || meths.size() == 0) {
986 env.failure(id +
987 " is not a valid line number or " +
988 "method name for class " +
989 refType.name());
990 return;
991 } else if (meths.size() > 1) {
992 env.failure(id +
993 " is an ambiguous method name in" +
994 refType.name());
995 return;
996 }
997 loc = meths.get(0).location();
998 lineno = loc.lineNumber();
999 }
1000 }
1001 int startLine = (lineno > 4) ? lineno - 4 : 1;
1002 int endLine = startLine + 9;
1003 String sourceLine = source.sourceLine(lineno);
1004 if (sourceLine == null) {
1005 env.failure("" +
1006 lineno +
1007 " is an invalid line number for " +
1008 refType.name());
1009 } else {
1010 OutputSink out = env.getOutputSink();
1011 for (int i = startLine; i <= endLine; i++) {
1012 sourceLine = source.sourceLine(i);
1013 if (sourceLine == null) {
1014 break;
1015 }
1016 out.print(i);
1017 out.print("\t");
1018 if (i == lineno) {
1019 out.print("=> ");
1020 } else {
1021 out.print(" ");
1022 }
1023 out.println(sourceLine);
1024 }
1025 out.show();
1026 }
1027 }
1028
1029 // Command: use
1030 // Get or set the source file path list.
1031
1032 private void commandUse(StringTokenizer t) {
1033 if (!t.hasMoreTokens()) {
1034 out.println(sourceManager.getSourcePath().asString());
1035 } else {
1036 //### Should throw exception for invalid path.
1037 //### E.g., vetoable property change.
1038 sourceManager.setSourcePath(new SearchPath(t.nextToken()));
1039 }
1040 }
1041
1042 // Command: sourcepath
1043 // Get or set the source file path list. (Alternate to 'use'.)
1044
1045 private void commandSourcepath(StringTokenizer t) {
1046 if (!t.hasMoreTokens()) {
1047 out.println(sourceManager.getSourcePath().asString());
1048 } else {
1049 //### Should throw exception for invalid path.
1050 //### E.g., vetoable property change.
1051 sourceManager.setSourcePath(new SearchPath(t.nextToken()));
1052 }
1053 }
1054
1055 // Command: classpath
1056 // Get or set the class file path list.
1057
1058 private void commandClasspath(StringTokenizer t) {
1059 if (!t.hasMoreTokens()) {
1060 out.println(classManager.getClassPath().asString());
1061 } else {
1062 //### Should throw exception for invalid path.
1063 //### E.g., vetoable property change.
1064 classManager.setClassPath(new SearchPath(t.nextToken()));
1065 }
1066 }
1067
1068 // Command: view
1069 // Display source for source file or class.
1070
1071 private void commandView(StringTokenizer t) throws NoSessionException {
1072 if (!t.hasMoreTokens()) {
1073 env.error("Argument required");
1074 } else {
1075 String name = t.nextToken();
1076 if (name.endsWith(".java") ||
1077 name.indexOf(File.separatorChar) >= 0) {
1078 env.viewSource(name);
1079 } else {
1080 //### JDI crashes taking line number for class.
1081 /*****
1082 ReferenceType cls = findClass(name);
1083 if (cls != null) {
1084 env.viewLocation(cls.location());
1085 } else {
1086 env.failure("No such class");
1087 }
1088 *****/
1089 String fileName = name.replace('.', File.separatorChar) + ".java";
1090 env.viewSource(fileName);
1091 }
1092 }
1093 }
1094
1095 // Command: locals
1096 // Print all local variables in current stack frame.
1097
1098 private void commandLocals() throws NoSessionException {
1099 ThreadReference current = context.getCurrentThread();
1100 if (current == null) {
1101 env.failure("No default thread specified: " +
1102 "use the \"thread\" command first.");
1103 return;
1104 }
1105 StackFrame frame;
1106 try {
1107 frame = context.getCurrentFrame(current);
1108 if (frame == null) {
1109 env.failure("Thread has not yet created any stack frames.");
1110 return;
1111 }
1112 } catch (VMNotInterruptedException e) {
1113 env.failure("Target VM must be in interrupted state.");
1114 return;
1115 }
1116
1117 List<LocalVariable> vars;
1118 try {
1119 vars = frame.visibleVariables();
1120 if (vars == null || vars.size() == 0) {
1121 env.failure("No local variables");
1122 return;
1123 }
1124 } catch (AbsentInformationException e) {
1125 env.failure("Local variable information not available." +
1126 " Compile with -g to generate variable information");
1127 return;
1128 }
1129
1130 OutputSink out = env.getOutputSink();
1131 out.println("Method arguments:");
1132 for (LocalVariable var : vars) {
1133 if (var.isArgument()) {
1134 printVar(out, var, frame);
1135 }
1136 }
1137 out.println("Local variables:");
1138 for (LocalVariable var : vars) {
1139 if (!var.isArgument()) {
1140 printVar(out, var, frame);
1141 }
1142 }
1143 out.show();
1144 return;
1145 }
1146
1147 /**
1148 * Command: monitor
1149 * Monitor an expression
1150 */
1151 private void commandMonitor(StringTokenizer t) throws NoSessionException {
1152 if (!t.hasMoreTokens()) {
1153 env.error("Argument required");
1154 } else {
1155 env.getMonitorListModel().add(t.nextToken(""));
1156 }
1157 }
1158
1159 /**
1160 * Command: unmonitor
1161 * Unmonitor an expression
1162 */
1163 private void commandUnmonitor(StringTokenizer t) throws NoSessionException {
1164 if (!t.hasMoreTokens()) {
1165 env.error("Argument required");
1166 } else {
1167 env.getMonitorListModel().remove(t.nextToken(""));
1168 }
1169 }
1170
1171 // Print a stack variable.
1172
1173 private void printVar(OutputSink out, LocalVariable var, StackFrame frame) {
1174 out.print(" " + var.name());
1175 if (var.isVisible(frame)) {
1176 Value val = frame.getValue(var);
1177 out.println(" = " + val.toString());
1178 } else {
1179 out.println(" is not in scope");
1180 }
1181 }
1182
1183 // Command: print
1184 // Evaluate an expression.
1185
1186 private void commandPrint(StringTokenizer t, boolean dumpObject) throws NoSessionException {
1187 if (!t.hasMoreTokens()) {
1188 //### Probably confused if expresion contains whitespace.
1189 env.error("No expression specified.");
1190 return;
1191 }
1192 ThreadReference current = context.getCurrentThread();
1193 if (current == null) {
1194 env.failure("No default thread specified: " +
1195 "use the \"thread\" command first.");
1196 return;
1197 }
1198 StackFrame frame;
1199 try {
1200 frame = context.getCurrentFrame(current);
1201 if (frame == null) {
1202 env.failure("Thread has not yet created any stack frames.");
1203 return;
1204 }
1205 } catch (VMNotInterruptedException e) {
1206 env.failure("Target VM must be in interrupted state.");
1207 return;
1208 }
1209 while (t.hasMoreTokens()) {
1210 String expr = t.nextToken("");
1211 Value val = null;
1212 try {
1213 val = runtime.evaluate(frame, expr);
1214 } catch(Exception e) {
1215 env.error("Exception: " + e);
1216 //### Fix this!
1217 }
1218 if (val == null) {
1219 return; // Error message already printed
1220 }
1221 OutputSink out = env.getOutputSink();
1222 if (dumpObject && (val instanceof ObjectReference) &&
1223 !(val instanceof StringReference)) {
1224 ObjectReference obj = (ObjectReference)val;
1225 ReferenceType refType = obj.referenceType();
1226 out.println(expr + " = " + val.toString() + " {");
1227 dump(out, obj, refType, refType);
1228 out.println("}");
1229 } else {
1230 out.println(expr + " = " + val.toString());
1231 }
1232 out.show();
1233 }
1234 }
1235
1236 private void dump(OutputSink out,
1237 ObjectReference obj, ReferenceType refType,
1238 ReferenceType refTypeBase) {
1239 for (Field field : refType.fields()) {
1240 out.print(" ");
1241 if (!refType.equals(refTypeBase)) {
1242 out.print(refType.name() + ".");
1243 }
1244 out.print(field.name() + ": ");
1245 Object o = obj.getValue(field);
1246 out.println((o == null) ? "null" : o.toString()); // Bug ID 4374471
1247 }
1248 if (refType instanceof ClassType) {
1249 ClassType sup = ((ClassType)refType).superclass();
1250 if (sup != null) {
1251 dump(out, obj, sup, refTypeBase);
1252 }
1253 } else if (refType instanceof InterfaceType) {
1254 for (InterfaceType sup : ((InterfaceType)refType).superinterfaces()) {
1255 dump(out, obj, sup, refTypeBase);
1256 }
1257 }
1258 }
1259
1260 /*
1261 * Display help message.
1262 */
1263
1264 private void help() {
1265 out.println("** command list **");
1266 out.println("threads [threadgroup] -- list threads");
1267 out.println("thread <thread id> -- set default thread");
1268 out.println("suspend [thread id(s)] -- suspend threads (default: all)");
1269 out.println("resume [thread id(s)] -- resume threads (default: all)");
1270 out.println("where [thread id] | all -- dump a thread's stack");
1271 out.println("wherei [thread id] | all -- dump a thread's stack, with pc info");
1272 out.println("threadgroups -- list threadgroups");
1273 out.println("threadgroup <name> -- set current threadgroup\n");
1274// out.println("print <expression> -- print value of expression");
1275 out.println("dump <expression> -- print all object information\n");
1276// out.println("eval <expression> -- evaluate expression (same as print)");
1277 out.println("locals -- print all local variables in current stack frame\n");
1278 out.println("classes -- list currently known classes");
1279 out.println("methods <class id> -- list a class's methods\n");
1280 out.println("stop [in] <class id>.<method>[(argument_type,...)] -- set a breakpoint in a method");
1281 out.println("stop [at] <class id>:<line> -- set a breakpoint at a line");
1282 out.println("up [n frames] -- move up a thread's stack");
1283 out.println("down [n frames] -- move down a thread's stack");
1284 out.println("frame <frame-id> -- to a frame");
1285 out.println("clear <class id>.<method>[(argument_type,...)] -- clear a breakpoint in a method");
1286 out.println("clear <class id>:<line> -- clear a breakpoint at a line");
1287 out.println("clear -- list breakpoints");
1288 out.println("step -- execute current line");
1289 out.println("step up -- execute until the current method returns to its caller");
1290 out.println("stepi -- execute current instruction");
1291 out.println("next -- step one line (step OVER calls)");
1292 out.println("nexti -- step one instruction (step OVER calls)");
1293 out.println("cont -- continue execution from breakpoint\n");
1294// out.println("catch <class id> -- break for the specified exception");
1295// out.println("ignore <class id> -- ignore when the specified exception\n");
1296 out.println("view classname|filename -- display source file");
1297 out.println("list [line number|method] -- print source code context at line or method");
1298 out.println("use <source file path> -- display or change the source path\n");
1299//### new
1300 out.println("sourcepath <source file path> -- display or change the source path\n");
1301//### new
1302 out.println("classpath <class file path> -- display or change the class path\n");
1303 out.println("monitor <expression> -- evaluate an expression each time the program stops\n");
1304 out.println("unmonitor <monitor#> -- delete a monitor\n");
1305 out.println("read <filename> -- read and execute a command file\n");
1306// out.println("memory -- report memory usage");
1307// out.println("gc -- free unused objects\n");
1308 out.println("run <class> [args] -- start execution of a Java class");
1309 out.println("run -- re-execute last class run");
1310 out.println("load <class> [args] -- start execution of a Java class, initially suspended");
1311 out.println("load -- re-execute last class run, initially suspended");
1312 out.println("attach <portname> -- debug existing process\n");
1313 out.println("detach -- detach from debuggee process\n");
1314 out.println("kill <thread(group)> -- kill a thread or threadgroup\n");
1315 out.println("!! -- repeat last command");
1316 out.println("help (or ?) -- list commands");
1317 out.println("exit (or quit) -- exit debugger");
1318 }
1319
1320 /*
1321 * Execute a command.
1322 */
1323
1324 public void executeCommand(String command) {
1325 //### Treatment of 'out' here is dirty...
1326 out = env.getOutputSink();
1327 if (echo) {
1328 out.println(">>> " + command);
1329 }
1330 StringTokenizer t = new StringTokenizer(command);
1331 try {
1332 String cmd;
1333 if (t.hasMoreTokens()) {
1334 cmd = t.nextToken().toLowerCase();
1335 lastCommand = cmd;
1336 } else {
1337 cmd = lastCommand;
1338 }
1339 if (cmd.equals("print")) {
1340 commandPrint(t, false);
1341 } else if (cmd.equals("eval")) {
1342 commandPrint(t, false);
1343 } else if (cmd.equals("dump")) {
1344 commandPrint(t, true);
1345 } else if (cmd.equals("locals")) {
1346 commandLocals();
1347 } else if (cmd.equals("classes")) {
1348 commandClasses();
1349 } else if (cmd.equals("methods")) {
1350 commandMethods(t);
1351 } else if (cmd.equals("threads")) {
1352 commandThreads(t);
1353 } else if (cmd.equals("thread")) {
1354 commandThread(t);
1355 } else if (cmd.equals("suspend")) {
1356 commandSuspend(t);
1357 } else if (cmd.equals("resume")) {
1358 commandResume(t);
1359 } else if (cmd.equals("cont")) {
1360 commandCont();
1361 } else if (cmd.equals("threadgroups")) {
1362 commandThreadGroups();
1363 } else if (cmd.equals("threadgroup")) {
1364 commandThreadGroup(t);
1365 } else if (cmd.equals("run")) {
1366 commandRun(t);
1367 } else if (cmd.equals("load")) {
1368 commandLoad(t);
1369 } else if (cmd.equals("connect")) {
1370 commandConnect(t);
1371 } else if (cmd.equals("attach")) {
1372 commandAttach(t);
1373 } else if (cmd.equals("detach")) {
1374 commandDetach(t);
1375 } else if (cmd.equals("interrupt")) {
1376 commandInterrupt(t);
1377//### Not implemented.
1378// } else if (cmd.equals("catch")) {
1379// commandCatchException(t);
1380//### Not implemented.
1381// } else if (cmd.equals("ignore")) {
1382// commandIgnoreException(t);
1383 } else if (cmd.equals("step")) {
1384 commandStep(t);
1385 } else if (cmd.equals("stepi")) {
1386 commandStepi();
1387 } else if (cmd.equals("next")) {
1388 commandNext();
1389 } else if (cmd.equals("nexti")) {
1390 commandNexti();
1391 } else if (cmd.equals("kill")) {
1392 commandKill(t);
1393 } else if (cmd.equals("where")) {
1394 commandWhere(t, false);
1395 } else if (cmd.equals("wherei")) {
1396 commandWhere(t, true);
1397 } else if (cmd.equals("up")) {
1398 commandUp(t);
1399 } else if (cmd.equals("down")) {
1400 commandDown(t);
1401 } else if (cmd.equals("frame")) {
1402 commandFrame(t);
1403 } else if (cmd.equals("stop")) {
1404 commandStop(t);
1405 } else if (cmd.equals("clear")) {
1406 commandClear(t);
1407 } else if (cmd.equals("list")) {
1408 commandList(t);
1409 } else if (cmd.equals("use")) {
1410 commandUse(t);
1411 } else if (cmd.equals("sourcepath")) {
1412 commandSourcepath(t);
1413 } else if (cmd.equals("classpath")) {
1414 commandClasspath(t);
1415 } else if (cmd.equals("monitor")) {
1416 commandMonitor(t);
1417 } else if (cmd.equals("unmonitor")) {
1418 commandUnmonitor(t);
1419 } else if (cmd.equals("view")) {
1420 commandView(t);
1421// } else if (cmd.equals("read")) {
1422// readCommand(t);
1423 } else if (cmd.equals("help") || cmd.equals("?")) {
1424 help();
1425 } else if (cmd.equals("quit") || cmd.equals("exit")) {
1426 try {
1427 runtime.detach();
1428 } catch (NoSessionException e) {
1429 // ignore
1430 }
1431 env.terminate();
1432 } else {
1433 //### Dubious repeat-count feature inherited from 'jdb'
1434 if (t.hasMoreTokens()) {
1435 try {
1436 int repeat = Integer.parseInt(cmd);
1437 String subcom = t.nextToken("");
1438 while (repeat-- > 0) {
1439 executeCommand(subcom);
1440 }
1441 return;
1442 } catch (NumberFormatException exc) {
1443 }
1444 }
1445 out.println("huh? Try help...");
1446 out.flush();
1447 }
1448 } catch (NoSessionException e) {
1449 out.println("There is no currently attached VM session.");
1450 out.flush();
1451 } catch (Exception e) {
1452 out.println("Internal exception: " + e.toString());
1453 out.flush();
1454 System.out.println("JDB internal exception: " + e.toString());
1455 e.printStackTrace();
1456 }
1457 out.show();
1458 }
1459}