1 /*
2  * Copyright (c) 2005, 2019, 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.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  *
23  */
24 
25 package sun.jvm.hotspot;
26 
27 import java.io.BufferedOutputStream;
28 import java.io.BufferedReader;
29 import java.io.ByteArrayOutputStream;
30 import java.io.FileInputStream;
31 import java.io.FileOutputStream;
32 import java.io.IOException;
33 import java.io.InputStreamReader;
34 import java.io.PrintStream;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.Comparator;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.Iterator;
41 import java.util.Stack;
42 import java.util.regex.Matcher;
43 import java.util.regex.Pattern;
44 
45 import sun.jvm.hotspot.ci.ciEnv;
46 import sun.jvm.hotspot.code.CodeBlob;
47 import sun.jvm.hotspot.code.CodeCacheVisitor;
48 import sun.jvm.hotspot.code.NMethod;
49 import sun.jvm.hotspot.debugger.Address;
50 import sun.jvm.hotspot.debugger.OopHandle;
51 import sun.jvm.hotspot.classfile.ClassLoaderDataGraph;
52 import sun.jvm.hotspot.memory.FileMapInfo;
53 import sun.jvm.hotspot.memory.SystemDictionary;
54 import sun.jvm.hotspot.memory.Universe;
55 import sun.jvm.hotspot.gc.shared.CollectedHeap;
56 import sun.jvm.hotspot.gc.g1.G1CollectedHeap;
57 import sun.jvm.hotspot.oops.DefaultHeapVisitor;
58 import sun.jvm.hotspot.oops.HeapVisitor;
59 import sun.jvm.hotspot.oops.InstanceKlass;
60 import sun.jvm.hotspot.oops.Klass;
61 import sun.jvm.hotspot.oops.Metadata;
62 import sun.jvm.hotspot.oops.Method;
63 import sun.jvm.hotspot.oops.MethodData;
64 import sun.jvm.hotspot.oops.Oop;
65 import sun.jvm.hotspot.oops.RawHeapVisitor;
66 import sun.jvm.hotspot.oops.Symbol;
67 import sun.jvm.hotspot.oops.UnknownOopException;
68 import sun.jvm.hotspot.opto.Compile;
69 import sun.jvm.hotspot.opto.InlineTree;
70 import sun.jvm.hotspot.runtime.CompiledVFrame;
71 import sun.jvm.hotspot.runtime.CompilerThread;
72 import sun.jvm.hotspot.runtime.JavaThread;
73 import sun.jvm.hotspot.runtime.JavaVFrame;
74 import sun.jvm.hotspot.runtime.Threads;
75 import sun.jvm.hotspot.runtime.VM;
76 import sun.jvm.hotspot.tools.ObjectHistogram;
77 import sun.jvm.hotspot.tools.PMap;
78 import sun.jvm.hotspot.tools.PStack;
79 import sun.jvm.hotspot.tools.StackTrace;
80 import sun.jvm.hotspot.tools.jcore.ClassDump;
81 import sun.jvm.hotspot.tools.jcore.ClassFilter;
82 import sun.jvm.hotspot.types.CIntegerType;
83 import sun.jvm.hotspot.types.Field;
84 import sun.jvm.hotspot.types.Type;
85 import sun.jvm.hotspot.types.basic.BasicType;
86 import sun.jvm.hotspot.ui.classbrowser.HTMLGenerator;
87 import sun.jvm.hotspot.ui.tree.CTypeTreeNodeAdapter;
88 import sun.jvm.hotspot.ui.tree.OopTreeNodeAdapter;
89 import sun.jvm.hotspot.ui.tree.SimpleTreeNode;
90 import sun.jvm.hotspot.utilities.AddressOps;
91 import sun.jvm.hotspot.utilities.Assert;
92 import sun.jvm.hotspot.utilities.CompactHashTable;
93 import sun.jvm.hotspot.utilities.HeapProgressThunk;
94 import sun.jvm.hotspot.utilities.LivenessPathElement;
95 import sun.jvm.hotspot.utilities.MethodArray;
96 import sun.jvm.hotspot.utilities.ObjectReader;
97 import sun.jvm.hotspot.utilities.PointerFinder;
98 import sun.jvm.hotspot.utilities.PointerLocation;
99 import sun.jvm.hotspot.utilities.ReversePtrs;
100 import sun.jvm.hotspot.utilities.ReversePtrsAnalysis;
101 import sun.jvm.hotspot.utilities.RobustOopDeterminator;
102 import sun.jvm.hotspot.utilities.SystemDictionaryHelper;
103 import sun.jvm.hotspot.utilities.soql.JSJavaFactory;
104 import sun.jvm.hotspot.utilities.soql.JSJavaFactoryImpl;
105 import sun.jvm.hotspot.utilities.soql.JSJavaScriptEngine;
106 
107 public class CommandProcessor {
108 
109     volatile boolean quit;
110 
111     public abstract static class DebuggerInterface {
getAgent()112         public abstract HotSpotAgent getAgent();
isAttached()113         public abstract boolean isAttached();
attach(String pid)114         public abstract void attach(String pid);
attach(String java, String core)115         public abstract void attach(String java, String core);
detach()116         public abstract void detach();
reattach()117         public abstract void reattach();
118     }
119 
120     public static class BootFilter implements ClassFilter {
canInclude(InstanceKlass kls)121         public boolean canInclude(InstanceKlass kls) {
122             return kls.getClassLoader() == null;
123         }
124     }
125 
126     public static class NonBootFilter implements ClassFilter {
127         private HashMap emitted = new HashMap();
canInclude(InstanceKlass kls)128         public boolean canInclude(InstanceKlass kls) {
129             if (kls.getClassLoader() == null) return false;
130             if (emitted.get(kls.getName()) != null) {
131                 // Since multiple class loaders are being shoved
132                 // together duplicate classes are a possibilty.  For
133                 // now just ignore them.
134                 return false;
135             }
136             emitted.put(kls.getName(), kls);
137             return true;
138         }
139     }
140 
141     static class Tokens {
142         final String input;
143         int i;
144         String[] tokens;
145         int length;
146 
splitWhitespace(String cmd)147         String[] splitWhitespace(String cmd) {
148             String[] t = cmd.split("\\s");
149             if (t.length == 1 && t[0].length() == 0) {
150                 return new String[0];
151             }
152             return t;
153         }
154 
add(String s, ArrayList t)155         void add(String s, ArrayList t) {
156             if (s.length() > 0) {
157                 t.add(s);
158             }
159         }
160 
Tokens(String cmd)161         Tokens(String cmd) {
162             input = cmd;
163 
164             // check for quoting
165             int quote = cmd.indexOf('"');
166             ArrayList t = new ArrayList();
167             if (quote != -1) {
168                 while (cmd.length() > 0) {
169                     if (quote != -1) {
170                         int endquote = cmd.indexOf('"', quote + 1);
171                         if (endquote == -1) {
172                             throw new RuntimeException("mismatched quotes: " + input);
173                         }
174 
175                         String before = cmd.substring(0, quote).trim();
176                         String quoted = cmd.substring(quote + 1, endquote);
177                         cmd = cmd.substring(endquote + 1).trim();
178                         if (before.length() > 0) {
179                             String[] w = splitWhitespace(before);
180                             for (int i = 0; i < w.length; i++) {
181                                 add(w[i], t);
182                             }
183                         }
184                         add(quoted, t);
185                         quote = cmd.indexOf('"');
186                     } else {
187                         String[] w = splitWhitespace(cmd);
188                         for (int i = 0; i < w.length; i++) {
189                             add(w[i], t);
190                         }
191                         cmd = "";
192 
193                     }
194                 }
195             } else {
196                 String[] w = splitWhitespace(cmd);
197                 for (int i = 0; i < w.length; i++) {
198                     add(w[i], t);
199                 }
200             }
201             tokens = (String[])t.toArray(new String[0]);
202             i = 0;
203             length = tokens.length;
204 
205             //for (int i = 0; i < tokens.length; i++) {
206             //    System.out.println("\"" + tokens[i] + "\"");
207             //}
208         }
209 
nextToken()210         String nextToken() {
211             return tokens[i++];
212         }
hasMoreTokens()213         boolean hasMoreTokens() {
214             return i < length;
215         }
countTokens()216         int countTokens() {
217             return length - i;
218         }
trim(int n)219         void trim(int n) {
220             if (length >= n) {
221                 length -= n;
222             } else {
223                 throw new IndexOutOfBoundsException(String.valueOf(n));
224             }
225         }
join(String sep)226         String join(String sep) {
227             StringBuffer result = new StringBuffer();
228             for (int w = i; w < length; w++) {
229                 result.append(tokens[w]);
230                 if (w + 1 < length) {
231                     result.append(sep);
232                 }
233             }
234             return result.toString();
235         }
236 
at(int i)237         String at(int i) {
238             if (i < 0 || i >= length) {
239                 throw new IndexOutOfBoundsException(String.valueOf(i));
240             }
241             return tokens[i];
242         }
243     }
244 
245 
246     abstract class Command {
Command(String n, String u, boolean ok)247         Command(String n, String u, boolean ok) {
248             name = n;
249             usage = u;
250             okIfDisconnected = ok;
251         }
252 
Command(String n, boolean ok)253         Command(String n, boolean ok) {
254             name = n;
255             usage = n;
256             okIfDisconnected = ok;
257         }
258 
259         final String name;
260         final String usage;
261         final boolean okIfDisconnected;
doit(Tokens t)262         abstract void doit(Tokens t);
usage()263         void usage() {
264             out.println("Usage: " + usage);
265         }
266 
printOopValue(Oop oop)267         void printOopValue(Oop oop) {
268             if (oop != null) {
269                 Klass k = oop.getKlass();
270                 Symbol s = k.getName();
271                 if (s != null) {
272                     out.print("Oop for " + s.asString() + " @ ");
273                 } else {
274                     out.print("Oop @ ");
275                 }
276                 Oop.printOopAddressOn(oop, out);
277             } else {
278                 out.print("null");
279             }
280         }
281 
printNode(SimpleTreeNode node)282         void printNode(SimpleTreeNode node) {
283             int count = node.getChildCount();
284             for (int i = 0; i < count; i++) {
285                 try {
286                     SimpleTreeNode field = node.getChild(i);
287                     if (field instanceof OopTreeNodeAdapter) {
288                         out.print(field);
289                         out.print(" ");
290                         printOopValue(((OopTreeNodeAdapter)field).getOop());
291                         out.println();
292                     } else {
293                         out.println(field);
294                     }
295                 } catch (Exception e) {
296                     out.println();
297                     out.println("Error: " + e);
298                     if (verboseExceptions) {
299                         e.printStackTrace(out);
300                     }
301                 }
302             }
303         }
304     }
305 
quote(String s)306     void quote(String s) {
307         if (s.indexOf(" ") == -1) {
308             out.print(s);
309         } else {
310             out.print("\"");
311             out.print(s);
312             out.print("\"");
313         }
314     }
315 
dumpType(Type type)316     void dumpType(Type type) {
317         out.print("type ");
318         quote(type.getName());
319         out.print(" ");
320         if (type.getSuperclass() != null) {
321             quote(type.getSuperclass().getName());
322             out.print(" ");
323         } else {
324             out.print("null ");
325         }
326         out.print(type.isOopType());
327         out.print(" ");
328         if (type.isCIntegerType()) {
329             out.print("true ");
330             out.print(((CIntegerType)type).isUnsigned());
331             out.print(" ");
332         } else {
333             out.print("false false ");
334         }
335         out.print(type.getSize());
336         out.println();
337     }
338 
dumpFields(Type type)339     void dumpFields(Type type) {
340         dumpFields(type, true);
341     }
342 
dumpFields(Type type, boolean allowStatic)343     void dumpFields(Type type, boolean allowStatic) {
344         Iterator i = type.getFields();
345         while (i.hasNext()) {
346             Field f = (Field) i.next();
347             if (!allowStatic && f.isStatic()) continue;
348             out.print("field ");
349             quote(type.getName());
350             out.print(" ");
351             out.print(f.getName());
352             out.print(" ");
353             quote(f.getType().getName());
354             out.print(" ");
355             out.print(f.isStatic());
356             out.print(" ");
357             if (f.isStatic()) {
358                 out.print("0 ");
359                 out.print(f.getStaticFieldAddress());
360             } else {
361                 out.print(f.getOffset());
362                 out.print(" 0x0");
363             }
364             out.println();
365         }
366     }
367 
368 
lookup(String symbol)369     Address lookup(String symbol) {
370         if (symbol.indexOf("::") != -1) {
371             String[] parts = symbol.split("::");
372             StringBuffer mangled = new StringBuffer("__1c");
373             for (int i = 0; i < parts.length; i++) {
374                 int len = parts[i].length();
375                 if (len >= 26) {
376                     mangled.append((char)('a' + (len / 26)));
377                     len = len % 26;
378                 }
379                 mangled.append((char)('A' + len));
380                 mangled.append(parts[i]);
381             }
382             mangled.append("_");
383             symbol = mangled.toString();
384         }
385         return VM.getVM().getDebugger().lookup(null, symbol);
386     }
387 
parseAddress(String addr)388     Address parseAddress(String addr) {
389         return VM.getVM().getDebugger().parseAddress(addr);
390     }
391 
392     private final Command[] commandList = {
393         new Command("reattach", true) {
394             public void doit(Tokens t) {
395                 int tokens = t.countTokens();
396                 if (tokens != 0) {
397                     usage();
398                     return;
399                 }
400                 preAttach();
401                 debugger.reattach();
402                 postAttach();
403             }
404         },
405         new Command("attach", "attach pid | exec core", true) {
406             public void doit(Tokens t) {
407                 int tokens = t.countTokens();
408                 if (tokens == 1) {
409                     preAttach();
410                     debugger.attach(t.nextToken());
411                     postAttach();
412                 } else if (tokens == 2) {
413                     preAttach();
414                     debugger.attach(t.nextToken(), t.nextToken());
415                     postAttach();
416                 } else {
417                     usage();
418                 }
419             }
420         },
421         new Command("detach", false) {
422             public void doit(Tokens t) {
423                 if (t.countTokens() != 0) {
424                     usage();
425                 } else {
426                     debugger.detach();
427                 }
428             }
429         },
430         new Command("examine", "examine [ address/count ] | [ address,address]", false) {
431             Pattern args1 = Pattern.compile("^(0x[0-9a-f]+)(/([0-9]*)([a-z]*))?$");
432             Pattern args2 = Pattern.compile("^(0x[0-9a-f]+),(0x[0-9a-f]+)(/[a-z]*)?$");
433 
434             String fill(Address a, int width) {
435                 String s = "0x0";
436                 if (a != null) {
437                     s = a.toString();
438                 }
439                 if (s.length() != width) {
440                     return s.substring(0, 2) + "000000000000000000000".substring(0, width - s.length()) + s.substring(2);
441                 }
442                 return s;
443             }
444 
445             public void doit(Tokens t) {
446                 if (t.countTokens() != 1) {
447                     usage();
448                 } else {
449                     String arg = t.nextToken();
450                     Matcher m1 = args1.matcher(arg);
451                     Matcher m2 = args2.matcher(arg);
452                     Address start = null;
453                     Address end   = null;
454                     String format = "";
455                     int formatSize = (int)VM.getVM().getAddressSize();
456 
457                     if (m1.matches()) {
458                         start = VM.getVM().getDebugger().parseAddress(m1.group(1));
459                         int count = 1;
460                         if (m1.group(2) != null) {
461                             count = Integer.parseInt(m1.group(3));
462                         }
463                         end = start.addOffsetTo(count * formatSize);
464                     } else if (m2.matches()) {
465                         start = VM.getVM().getDebugger().parseAddress(m2.group(1));
466                         end   = VM.getVM().getDebugger().parseAddress(m2.group(2));
467                     } else {
468                         usage();
469                         return;
470                     }
471                     int line = 80;
472                     int formatWidth = formatSize * 8 / 4 + 2;
473 
474                     out.print(fill(start, formatWidth));
475                     out.print(": ");
476                     int width = line - formatWidth - 2;
477 
478                     boolean needsPrintln = true;
479                     while (start != null && start.lessThan(end)) {
480                         Address val = start.getAddressAt(0);
481                         out.print(fill(val, formatWidth));
482                         needsPrintln = true;
483                         width -= formatWidth;
484                         start = start.addOffsetTo(formatSize);
485                         if (width <= formatWidth) {
486                             out.println();
487                             needsPrintln = false;
488                             if (start.lessThan(end)) {
489                                 out.print(fill(start, formatWidth));
490                                 out.print(": ");
491                                 width = line - formatWidth - 2;
492                             }
493                         } else {
494                             out.print(" ");
495                             width -= 1;
496                         }
497                     }
498                     if (needsPrintln) {
499                         out.println();
500                     }
501                 }
502             }
503         },
504         new Command("dumpreplaydata", "dumpreplaydata { <address > | -a | <thread_id> }", false) {
505             // This is used to dump replay data from ciInstanceKlass, ciMethodData etc
506             // default file name is replay.txt, also if java crashes in compiler
507             // thread, this file will be dumped in error processing.
508             public void doit(Tokens t) {
509                 if (t.countTokens() != 1) {
510                     usage();
511                     return;
512                 }
513                 String name = t.nextToken();
514                 Address a = null;
515                 try {
516                     a = VM.getVM().getDebugger().parseAddress(name);
517                 } catch (NumberFormatException e) { }
518                 if (a != null) {
519                     // only nmethod, Method, MethodData and InstanceKlass needed to
520                     // dump replay data
521 
522                     CodeBlob cb = VM.getVM().getCodeCache().findBlob(a);
523                     if (cb != null && (cb instanceof NMethod)) {
524                         ((NMethod)cb).dumpReplayData(out);
525                         return;
526                     }
527                     // assume it is Metadata
528                     Metadata meta = Metadata.instantiateWrapperFor(a);
529                     if (meta != null) {
530                         meta.dumpReplayData(out);
531                     } else {
532                         usage();
533                         return;
534                     }
535                 }
536                 // Not an address
537                 boolean all = name.equals("-a");
538                 Threads threads = VM.getVM().getThreads();
539                 for (int i = 0; i < threads.getNumberOfThreads(); i++) {
540                     JavaThread thread = threads.getJavaThreadAt(i);
541                     ByteArrayOutputStream bos = new ByteArrayOutputStream();
542                     thread.printThreadIDOn(new PrintStream(bos));
543                     if (all || bos.toString().equals(name)) {
544                         if (thread instanceof CompilerThread) {
545                             CompilerThread ct = (CompilerThread)thread;
546                             ciEnv env = ct.env();
547                             if (env != null) {
548                                env.dumpReplayData(out);
549                             }
550                         }
551                     }
552                 }
553             }
554         },
555         new Command("buildreplayjars", "buildreplayjars [ all | app | boot ]  | [ prefix ]", false) {
556             // This is used to dump jar files of all the classes
557             // loaded in the core.  Everything with null classloader
558             // will go in boot.jar and everything else will go in
559             // app.jar. boot.jar usually not needed, unless changed by jvmti.
560             public void doit(Tokens t) {
561                 int tcount = t.countTokens();
562                 if (tcount > 2) {
563                     usage();
564                     return;
565                 }
566                 try {
567                    String prefix = "";
568                    String option = "all"; // default
569                    switch(tcount) {
570                        case 0:
571                            break;
572                        case 1:
573                            option = t.nextToken();
574                            if (!option.equalsIgnoreCase("all") && !option.equalsIgnoreCase("app") &&
575                                !option.equalsIgnoreCase("root")) {
576                               prefix = option;
577                               option = "all";
578                            }
579                            break;
580                        case 2:
581                            option = t.nextToken();
582                            prefix = t.nextToken();
583                            break;
584                        default:
585                            usage();
586                            return;
587                    }
588                    if (!option.equalsIgnoreCase("all") && !option.equalsIgnoreCase("app") &&
589                                !option.equalsIgnoreCase("boot")) {
590                        usage();
591                        return;
592                    }
593                    ClassDump cd = new ClassDump();
594                    if (option.equalsIgnoreCase("all") || option.equalsIgnoreCase("boot")) {
595                      cd.setClassFilter(new BootFilter());
596                      cd.setJarOutput(prefix + "boot.jar");
597                      cd.run();
598                    }
599                    if (option.equalsIgnoreCase("all") || option.equalsIgnoreCase("app")) {
600                      cd.setClassFilter(new NonBootFilter());
601                      cd.setJarOutput(prefix + "app.jar");
602                      cd.run();
603                    }
604                 } catch (IOException ioe) {
605                    ioe.printStackTrace();
606                 }
607             }
608         },
609         new Command("findpc", "findpc address", false) {
610             public void doit(Tokens t) {
611                 if (t.countTokens() != 1) {
612                     usage();
613                 } else {
614                     Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
615                     PointerLocation loc = PointerFinder.find(a);
616                     loc.printOn(out);
617                 }
618             }
619         },
620         new Command("symbol", "symbol address", false) {
621             public void doit(Tokens t) {
622                 if (t.countTokens() != 1) {
623                     usage();
624                 } else {
625                     Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
626                     Symbol.create(a).printValueOn(out);
627                     out.println();
628                 }
629             }
630         },
631         new Command("flags", "flags [ flag | -nd ]", false) {
632             public void doit(Tokens t) {
633                 int tokens = t.countTokens();
634                 if (tokens != 0 && tokens != 1) {
635                     usage();
636                 } else {
637                     String name = tokens > 0 ? t.nextToken() : null;
638                     boolean nonDefault = false;
639                     if (name != null && name.equals("-nd")) {
640                         name = null;
641                         nonDefault = true;
642                     }
643 
644                     VM.Flag[] flags = VM.getVM().getCommandLineFlags();
645                     if (flags == null) {
646                         out.println("Command Flag info not available (use 1.4.1_03 or later)!");
647                     } else {
648                         boolean printed = false;
649                         for (int f = 0; f < flags.length; f++) {
650                             VM.Flag flag = flags[f];
651                             if (name == null || flag.getName().equals(name)) {
652 
653                                 if (nonDefault && (flag.getOrigin() == VM.Flags_DEFAULT)) {
654                                     // only print flags which aren't their defaults
655                                     continue;
656                                 }
657                                 out.println(flag.getName() + " = " + flag.getValue() + " " + flag.getOriginString());
658                                 printed = true;
659                             }
660                         }
661                         if (name != null && !printed) {
662                             out.println("Couldn't find flag: " + name);
663                         }
664                     }
665                 }
666             }
667         },
668         new Command("help", "help [ command ]", true) {
669             public void doit(Tokens t) {
670                 int tokens = t.countTokens();
671                 Command cmd = null;
672                 if (tokens == 1) {
673                     cmd = findCommand(t.nextToken());
674                 }
675 
676                 if (cmd != null) {
677                     cmd.usage();
678                 } else if (tokens == 0) {
679                     out.println("Available commands:");
680                     Object[] keys = commands.keySet().toArray();
681                     Arrays.sort(keys, new Comparator() {
682                              public int compare(Object o1, Object o2) {
683                                  return o1.toString().compareTo(o2.toString());
684                              }
685                           });
686                     for (int i = 0; i < keys.length; i++) {
687                         out.print("  ");
688                         out.println(((Command)commands.get(keys[i])).usage);
689                     }
690                 }
691             }
692         },
693         new Command("history", "history", true) {
694             public void doit(Tokens t) {
695                 int tokens = t.countTokens();
696                 if (tokens != 0 && (tokens != 1 || !t.nextToken().equals("-h"))) {
697                     usage();
698                     return;
699                 }
700                 boolean printIndex = tokens == 0;
701                 for (int i = 0; i < history.size(); i++) {
702                     if (printIndex) out.print(i + " ");
703                     out.println(history.get(i));
704                 }
705             }
706         },
707         // decode raw address
708         new Command("dis", "dis address [length]", false) {
709             public void doit(Tokens t) {
710                 int tokens = t.countTokens();
711                 if (tokens != 1 && tokens != 2) {
712                     usage();
713                     return;
714                 }
715                 String name = t.nextToken();
716                 Address addr = null;
717                 int len = 0x10; // default length
718                 try {
719                     addr = VM.getVM().getDebugger().parseAddress(name);
720                 } catch (NumberFormatException e) {
721                    out.println(e);
722                    return;
723                 }
724                 if (tokens == 2) {
725                     try {
726                         len = Integer.parseInt(t.nextToken());
727                     } catch (NumberFormatException e) {
728                         out.println(e);
729                         return;
730                     }
731                 }
732                 HTMLGenerator generator = new HTMLGenerator(false);
733                 out.println(generator.genHTMLForRawDisassembly(addr, len));
734             }
735 
736         },
737         // decode codeblob or nmethod
738         new Command("disassemble", "disassemble address", false) {
739             public void doit(Tokens t) {
740                 int tokens = t.countTokens();
741                 if (tokens != 1) {
742                     usage();
743                     return;
744                 }
745                 String name = t.nextToken();
746                 Address addr = null;
747                 try {
748                     addr = VM.getVM().getDebugger().parseAddress(name);
749                 } catch (NumberFormatException e) {
750                    out.println(e);
751                    return;
752                 }
753 
754                 HTMLGenerator generator = new HTMLGenerator(false);
755                 out.println(generator.genHTML(addr));
756             }
757         },
758         // print Java bytecode disassembly
759         new Command("jdis", "jdis address", false) {
760             public void doit(Tokens t) {
761                 int tokens = t.countTokens();
762                 if (tokens != 1) {
763                     usage();
764                     return;
765                 }
766                 Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
767                 Method m = (Method)Metadata.instantiateWrapperFor(a);
768                 HTMLGenerator html = new HTMLGenerator(false);
769                 out.println(html.genHTML(m));
770             }
771         },
772         new Command("revptrs", "revptrs address", false) {
773             public void doit(Tokens t) {
774                 int tokens = t.countTokens();
775                 if (tokens != 1 && (tokens != 2 || !t.nextToken().equals("-c"))) {
776                     usage();
777                     return;
778                 }
779                 boolean chase = tokens == 2;
780                 ReversePtrs revptrs = VM.getVM().getRevPtrs();
781                 if (revptrs == null) {
782                     out.println("Computing reverse pointers...");
783                     ReversePtrsAnalysis analysis = new ReversePtrsAnalysis();
784                     final boolean[] complete = new boolean[1];
785                     HeapProgressThunk thunk = new HeapProgressThunk() {
786                             public void heapIterationFractionUpdate(double d) {}
787                             public synchronized void heapIterationComplete() {
788                                 complete[0] = true;
789                                 notify();
790                             }
791                         };
792                     analysis.setHeapProgressThunk(thunk);
793                     analysis.run();
794                     while (!complete[0]) {
795                         synchronized (thunk) {
796                             try {
797                                 thunk.wait();
798                             } catch (Exception e) {
799                             }
800                         }
801                     }
802                     revptrs = VM.getVM().getRevPtrs();
803                     out.println("Done.");
804                 }
805                 Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
806                 if (VM.getVM().getUniverse().heap().isInReserved(a)) {
807                     OopHandle handle = a.addOffsetToAsOopHandle(0);
808                     Oop oop = VM.getVM().getObjectHeap().newOop(handle);
809                     ArrayList ptrs = revptrs.get(oop);
810                     if (ptrs == null) {
811                         out.println("no live references to " + a);
812                     } else {
813                         if (chase) {
814                             while (ptrs.size() == 1) {
815                                 LivenessPathElement e = (LivenessPathElement)ptrs.get(0);
816                                 ByteArrayOutputStream bos = new ByteArrayOutputStream();
817                                 Oop.printOopValueOn(e.getObj(), new PrintStream(bos));
818                                 out.println(bos.toString());
819                                 ptrs = revptrs.get(e.getObj());
820                             }
821                         } else {
822                             for (int i = 0; i < ptrs.size(); i++) {
823                                 LivenessPathElement e = (LivenessPathElement)ptrs.get(i);
824                                 ByteArrayOutputStream bos = new ByteArrayOutputStream();
825                                 Oop.printOopValueOn(e.getObj(), new PrintStream(bos));
826                                 out.println(bos.toString());
827                                 oop = e.getObj();
828                             }
829                         }
830                     }
831                 }
832             }
833         },
834         new Command("printmdo", "printmdo [ -a | expression ]", false) {
835             // Print every MDO in the heap or the one referenced by expression.
836             public void doit(Tokens t) {
837                 if (t.countTokens() != 1) {
838                     usage();
839                 } else {
840                     String s = t.nextToken();
841                     if (s.equals("-a")) {
842                         ClassLoaderDataGraph cldg = VM.getVM().getClassLoaderDataGraph();
843                         cldg.classesDo(new ClassLoaderDataGraph.ClassVisitor() {
844                                 public void visit(Klass k) {
845                                     if (k instanceof InstanceKlass) {
846                                         MethodArray methods = ((InstanceKlass)k).getMethods();
847                                         for (int i = 0; i < methods.length(); i++) {
848                                             Method m = methods.at(i);
849                                             MethodData mdo = m.getMethodData();
850                                             if (mdo != null) {
851                                                 out.println("MethodData " + mdo.getAddress() + " for " +
852                                                     "method " + m.getMethodHolder().getName().asString() + "." +
853                                                     m.getName().asString() +
854                                                             m.getSignature().asString() + "@" + m.getAddress());
855                                                 mdo.printDataOn(out);
856                                     }
857                                 }
858                                     }
859                                 }
860                             }
861                             );
862                     } else {
863                         Address a = VM.getVM().getDebugger().parseAddress(s);
864                         MethodData mdo = (MethodData) Metadata.instantiateWrapperFor(a);
865                         mdo.printDataOn(out);
866                     }
867                 }
868             }
869         },
870         new Command("printall", "printall", false) {
871             // Print every MDO in the heap or the one referenced by expression.
872             public void doit(Tokens t) {
873                 if (t.countTokens() != 0) {
874                     usage();
875                 } else {
876                     ClassLoaderDataGraph cldg = VM.getVM().getClassLoaderDataGraph();
877                     cldg.classesDo(new ClassLoaderDataGraph.ClassVisitor() {
878                             public void visit(Klass k) {
879                                 if (k instanceof InstanceKlass && ((InstanceKlass)k).getConstants().getCache() != null) {
880                                     MethodArray methods = ((InstanceKlass)k).getMethods();
881                                     for (int i = 0; i < methods.length(); i++) {
882                                         Method m = methods.at(i);
883                                         HTMLGenerator gen = new HTMLGenerator(false);
884                                         out.println(gen.genHTML(m));
885                                     }
886                                 }
887                             }
888                         }
889                         );
890                 }
891             }
892         },
893         new Command("dumpideal", "dumpideal { -a | id }", false) {
894             // Do a full dump of the nodes reachabile from root in each compiler thread.
895             public void doit(Tokens t) {
896                 if (t.countTokens() != 1) {
897                     usage();
898                 } else {
899                     String name = t.nextToken();
900                     boolean all = name.equals("-a");
901                     Threads threads = VM.getVM().getThreads();
902                     for (int i = 0; i < threads.getNumberOfThreads(); i++) {
903                         JavaThread thread = threads.getJavaThreadAt(i);
904                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
905                         thread.printThreadIDOn(new PrintStream(bos));
906                         if (all || bos.toString().equals(name)) {
907                           if (thread instanceof CompilerThread) {
908                             CompilerThread ct = (CompilerThread)thread;
909                             out.println(ct);
910                             ciEnv env = ct.env();
911                             if (env != null) {
912                               Compile c = env.compilerData();
913                               c.root().dump(9999, out);
914                             } else {
915                               out.println("  not compiling");
916                             }
917                           }
918                         }
919                     }
920                 }
921             }
922         },
923         new Command("dumpcfg", "dumpcfg { -a | id }", false) {
924             // Dump the PhaseCFG for every compiler thread that has one live.
925             public void doit(Tokens t) {
926                 if (t.countTokens() != 1) {
927                     usage();
928                 } else {
929                     String name = t.nextToken();
930                     boolean all = name.equals("-a");
931                     Threads threads = VM.getVM().getThreads();
932                     for (int i = 0; i < threads.getNumberOfThreads(); i++) {
933                         JavaThread thread = threads.getJavaThreadAt(i);
934                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
935                         thread.printThreadIDOn(new PrintStream(bos));
936                         if (all || bos.toString().equals(name)) {
937                           if (thread instanceof CompilerThread) {
938                             CompilerThread ct = (CompilerThread)thread;
939                             out.println(ct);
940                             ciEnv env = ct.env();
941                             if (env != null) {
942                               Compile c = env.compilerData();
943                               c.cfg().dump(out);
944                             }
945                           }
946                         }
947                     }
948                 }
949             }
950         },
951         new Command("dumpilt", "dumpilt { -a | id }", false) {
952             // dumps the InlineTree of a C2 compile
953             public void doit(Tokens t) {
954                 if (t.countTokens() != 1) {
955                     usage();
956                 } else {
957                     String name = t.nextToken();
958                     boolean all = name.equals("-a");
959                     Threads threads = VM.getVM().getThreads();
960                     for (int i = 0; i < threads.getNumberOfThreads(); i++) {
961                         JavaThread thread = threads.getJavaThreadAt(i);
962                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
963                         thread.printThreadIDOn(new PrintStream(bos));
964                         if (all || bos.toString().equals(name)) {
965                             if (thread instanceof CompilerThread) {
966                                 CompilerThread ct = (CompilerThread)thread;
967                                 ciEnv env = ct.env();
968                                 if (env != null) {
969                                     Compile c = env.compilerData();
970                                     InlineTree ilt = c.ilt();
971                                     if (ilt != null) {
972                                         ilt.print(out);
973                                     }
974                                 }
975                             }
976                         }
977                     }
978                 }
979             }
980         },
981         new Command("vmstructsdump", "vmstructsdump", false) {
982             public void doit(Tokens t) {
983                 if (t.countTokens() != 0) {
984                     usage();
985                     return;
986                 }
987 
988                 // Dump a copy of the type database in a form that can
989                 // be read back.
990                 Iterator i = agent.getTypeDataBase().getTypes();
991                 // Make sure the types are emitted in an order than can be read back in
992                 HashSet emitted = new HashSet();
993                 Stack pending = new Stack();
994                 while (i.hasNext()) {
995                     Type n = (Type)i.next();
996                     if (emitted.contains(n.getName())) {
997                         continue;
998                     }
999 
1000                     while (n != null && !emitted.contains(n.getName())) {
1001                         pending.push(n);
1002                         n = n.getSuperclass();
1003                     }
1004                     while (!pending.empty()) {
1005                         n = (Type)pending.pop();
1006                         dumpType(n);
1007                         emitted.add(n.getName());
1008                     }
1009                 }
1010                 i = agent.getTypeDataBase().getTypes();
1011                 while (i.hasNext()) {
1012                     dumpFields((Type)i.next(), false);
1013                 }
1014             }
1015         },
1016 
1017         new Command("inspect", "inspect expression", false) {
1018             public void doit(Tokens t) {
1019                 if (t.countTokens() != 1) {
1020                     usage();
1021                 } else {
1022                     Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
1023                     SimpleTreeNode node = null;
1024                     if (VM.getVM().getUniverse().heap().isInReserved(a)) {
1025                         OopHandle handle = a.addOffsetToAsOopHandle(0);
1026                         Oop oop = VM.getVM().getObjectHeap().newOop(handle);
1027                         node = new OopTreeNodeAdapter(oop, null);
1028 
1029                         out.println("instance of " + node.getValue() + " @ " + a +
1030                                     " (size = " + oop.getObjectSize() + ")");
1031                     } else if (VM.getVM().getCodeCache().contains(a)) {
1032                         CodeBlob blob = VM.getVM().getCodeCache().findBlobUnsafe(a);
1033                         a = blob.headerBegin();
1034                     }
1035                     if (node == null) {
1036                         Type type = VM.getVM().getTypeDataBase().guessTypeForAddress(a);
1037                         if (type == null && VM.getVM().isSharingEnabled()) {
1038                             // Check if the value falls in the _md_region
1039                             Address loc1 = a.getAddressAt(0);
1040                             FileMapInfo cdsFileMapInfo = VM.getVM().getFileMapInfo();
1041                             if (cdsFileMapInfo.inCopiedVtableSpace(loc1)) {
1042                                type = cdsFileMapInfo.getTypeForVptrAddress(loc1);
1043                             }
1044 
1045                         }
1046                         if (type != null) {
1047                             out.println("Type is " + type.getName() + " (size of " + type.getSize() + ")");
1048                             node = new CTypeTreeNodeAdapter(a, type, null);
1049                         }
1050                     }
1051                     if (node != null) {
1052                         printNode(node);
1053                     }
1054                 }
1055             }
1056         },
1057         new Command("jhisto", "jhisto", false) {
1058             public void doit(Tokens t) {
1059                  ObjectHistogram histo = new ObjectHistogram();
1060                  histo.run(out, err);
1061             }
1062         },
1063         new Command("jstack", "jstack [-v]", false) {
1064             public void doit(Tokens t) {
1065                 boolean verbose = false;
1066                 if (t.countTokens() > 0 && t.nextToken().equals("-v")) {
1067                     verbose = true;
1068                 }
1069                 StackTrace jstack = new StackTrace(verbose, true);
1070                 jstack.run(out);
1071             }
1072         },
1073         new Command("print", "print expression", false) {
1074             public void doit(Tokens t) {
1075                 if (t.countTokens() != 1) {
1076                     usage();
1077                 } else {
1078                     Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
1079                     HTMLGenerator gen = new HTMLGenerator(false);
1080                     out.println(gen.genHTML(a));
1081                 }
1082             }
1083         },
1084         new Command("printas", "printas type expression", false) {
1085             public void doit(Tokens t) {
1086                 if (t.countTokens() != 2) {
1087                     usage();
1088                 } else {
1089                     Type type = agent.getTypeDataBase().lookupType(t.nextToken());
1090                     Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
1091                     CTypeTreeNodeAdapter node = new CTypeTreeNodeAdapter(a, type, null);
1092 
1093                     out.println("pointer to " + type + " @ " + a +
1094                                 " (size = " + type.getSize() + ")");
1095                     printNode(node);
1096                 }
1097             }
1098         },
1099         new Command("printstatics", "printstatics [ type ]", false) {
1100             public void doit(Tokens t) {
1101                 if (t.countTokens() > 1) {
1102                     usage();
1103                 } else {
1104                     if (t.countTokens() == 0) {
1105                         out.println("All known static fields");
1106                         printNode(new CTypeTreeNodeAdapter(agent.getTypeDataBase().getTypes()));
1107                     } else {
1108                         Type type = agent.getTypeDataBase().lookupType(t.nextToken());
1109                         out.println("Static fields of " + type.getName());
1110                         printNode(new CTypeTreeNodeAdapter(type));
1111                     }
1112                 }
1113             }
1114         },
1115         new Command("pmap", "pmap", false) {
1116             public void doit(Tokens t) {
1117                 PMap pmap = new PMap();
1118                 pmap.run(out, debugger.getAgent().getDebugger());
1119             }
1120         },
1121         new Command("pstack", "pstack [-v]", false) {
1122             public void doit(Tokens t) {
1123                 boolean verbose = false;
1124                 if (t.countTokens() > 0 && t.nextToken().equals("-v")) {
1125                     verbose = true;
1126                 }
1127                 PStack pstack = new PStack(verbose, true);
1128                 pstack.run(out, debugger.getAgent().getDebugger());
1129             }
1130         },
1131         new Command("quit", true) {
1132             public void doit(Tokens t) {
1133                 if (t.countTokens() != 0) {
1134                     usage();
1135                 } else {
1136                     debugger.detach();
1137                     quit = true;
1138                 }
1139             }
1140         },
1141         new Command("echo", "echo [ true | false ]", true) {
1142             public void doit(Tokens t) {
1143                 if (t.countTokens() == 0) {
1144                     out.println("echo is " + doEcho);
1145                 } else if (t.countTokens() == 1) {
1146                     doEcho = Boolean.valueOf(t.nextToken()).booleanValue();
1147                 } else {
1148                     usage();
1149                 }
1150             }
1151         },
1152         new Command("versioncheck", "versioncheck [ true | false ]", true) {
1153             public void doit(Tokens t) {
1154                 if (t.countTokens() == 0) {
1155                     out.println("versioncheck is " +
1156                                 (System.getProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck") == null));
1157                 } else if (t.countTokens() == 1) {
1158                     if (Boolean.valueOf(t.nextToken()).booleanValue()) {
1159                         System.setProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck", null);
1160                     } else {
1161                         System.setProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck", "true");
1162                     }
1163                 } else {
1164                     usage();
1165                 }
1166             }
1167         },
1168         new Command("scanoops", "scanoops start end [ type ]", false) {
1169             public void doit(Tokens t) {
1170                 if (t.countTokens() != 2 && t.countTokens() != 3) {
1171                     usage();
1172                 } else {
1173                     long stride = VM.getVM().getAddressSize();
1174                     Address base = VM.getVM().getDebugger().parseAddress(t.nextToken());
1175                     Address end  = VM.getVM().getDebugger().parseAddress(t.nextToken());
1176                     Klass klass = null;
1177                     if (t.countTokens() == 1) {
1178                         klass = SystemDictionaryHelper.findInstanceKlass(t.nextToken());
1179                         if (klass == null) {
1180                             out.println("No such type.");
1181                             return;
1182                         }
1183                     }
1184                     while (base != null && base.lessThan(end)) {
1185                         long step = stride;
1186                         OopHandle handle = base.addOffsetToAsOopHandle(0);
1187                         if (RobustOopDeterminator.oopLooksValid(handle)) {
1188                             try {
1189                                 Oop oop = VM.getVM().getObjectHeap().newOop(handle);
1190                                 if (klass == null || oop.getKlass().isSubtypeOf(klass))
1191                                     out.println(handle.toString() + " " + oop.getKlass().getName().asString());
1192                                 step = oop.getObjectSize();
1193                             } catch (UnknownOopException ex) {
1194                                 // ok
1195                             } catch (RuntimeException ex) {
1196                                 ex.printStackTrace();
1197                             }
1198                         }
1199                         base = base.addOffsetTo(step);
1200                     }
1201                 }
1202             }
1203         },
1204         new Command("intConstant", "intConstant [ name [ value ] ]", true) {
1205             public void doit(Tokens t) {
1206                 if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 2) {
1207                     usage();
1208                     return;
1209                 }
1210                 HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
1211                 if (t.countTokens() == 1) {
1212                     String name = t.nextToken();
1213                     out.println("intConstant " + name + " " + db.lookupIntConstant(name));
1214                 } else if (t.countTokens() == 0) {
1215                     Iterator i = db.getIntConstants();
1216                     while (i.hasNext()) {
1217                         String name = (String)i.next();
1218                         out.println("intConstant " + name + " " + db.lookupIntConstant(name));
1219                     }
1220                 } else if (t.countTokens() == 2) {
1221                     String name = t.nextToken();
1222                     Integer value = Integer.valueOf(t.nextToken());
1223                     db.addIntConstant(name, value);
1224                 }
1225             }
1226         },
1227         new Command("longConstant", "longConstant [ name [ value ] ]", true) {
1228             public void doit(Tokens t) {
1229                 if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 2) {
1230                     usage();
1231                     return;
1232                 }
1233                 HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
1234                 if (t.countTokens() == 1) {
1235                     String name = t.nextToken();
1236                     out.println("longConstant " + name + " " + db.lookupLongConstant(name));
1237                 } else if (t.countTokens() == 0) {
1238                     Iterator i = db.getLongConstants();
1239                     while (i.hasNext()) {
1240                         String name = (String)i.next();
1241                         out.println("longConstant " + name + " " + db.lookupLongConstant(name));
1242                     }
1243                 } else if (t.countTokens() == 2) {
1244                     String name = t.nextToken();
1245                     Long value = Long.valueOf(t.nextToken());
1246                     db.addLongConstant(name, value);
1247                 }
1248             }
1249         },
1250         new Command("field", "field [ type [ name fieldtype isStatic offset address ] ]", true) {
1251             public void doit(Tokens t) {
1252                 if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 6) {
1253                     usage();
1254                     return;
1255                 }
1256                 if (t.countTokens() == 1) {
1257                     Type type = agent.getTypeDataBase().lookupType(t.nextToken());
1258                     dumpFields(type);
1259                 } else if (t.countTokens() == 0) {
1260                     Iterator i = agent.getTypeDataBase().getTypes();
1261                     while (i.hasNext()) {
1262                         dumpFields((Type)i.next());
1263                     }
1264                 } else {
1265                     BasicType containingType = (BasicType)agent.getTypeDataBase().lookupType(t.nextToken());
1266 
1267                     String fieldName = t.nextToken();
1268 
1269                     // The field's Type must already be in the database -- no exceptions
1270                     Type fieldType = agent.getTypeDataBase().lookupType(t.nextToken());
1271 
1272                     boolean isStatic = Boolean.valueOf(t.nextToken()).booleanValue();
1273                     long offset = Long.parseLong(t.nextToken());
1274                     Address staticAddress = parseAddress(t.nextToken());
1275                     if (isStatic && staticAddress == null) {
1276                         staticAddress = lookup(containingType.getName() + "::" + fieldName);
1277                     }
1278 
1279                     // check to see if the field already exists
1280                     Iterator i = containingType.getFields();
1281                     while (i.hasNext()) {
1282                         Field f = (Field) i.next();
1283                         if (f.getName().equals(fieldName)) {
1284                             if (f.isStatic() != isStatic) {
1285                                 throw new RuntimeException("static/nonstatic mismatch: " + t.input);
1286                             }
1287                             if (!isStatic) {
1288                                 if (f.getOffset() != offset) {
1289                                     throw new RuntimeException("bad redefinition of field offset: " + t.input);
1290                                 }
1291                             } else {
1292                                 if (!f.getStaticFieldAddress().equals(staticAddress)) {
1293                                     throw new RuntimeException("bad redefinition of field location: " + t.input);
1294                                 }
1295                             }
1296                             if (f.getType() != fieldType) {
1297                                 throw new RuntimeException("bad redefinition of field type: " + t.input);
1298                             }
1299                             return;
1300                         }
1301                     }
1302 
1303                     // Create field by type
1304                     HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
1305                     db.createField(containingType,
1306                                    fieldName, fieldType,
1307                                    isStatic,
1308                                    offset,
1309                                    staticAddress);
1310 
1311                 }
1312             }
1313 
1314         },
1315         new Command("tokenize", "tokenize ...", true) {
1316             public void doit(Tokens t) {
1317                 while (t.hasMoreTokens()) {
1318                     out.println("\"" + t.nextToken() + "\"");
1319                 }
1320             }
1321         },
1322         new Command("type", "type [ type [ name super isOop isInteger isUnsigned size ] ]", true) {
1323             public void doit(Tokens t) {
1324                 if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 6) {
1325                     usage();
1326                     return;
1327                 }
1328                 if (t.countTokens() == 6) {
1329                     String typeName = t.nextToken();
1330                     String superclassName = t.nextToken();
1331                     if (superclassName.equals("null")) {
1332                         superclassName = null;
1333                     }
1334                     boolean isOop = Boolean.valueOf(t.nextToken()).booleanValue();
1335                     boolean isInteger = Boolean.valueOf(t.nextToken()).booleanValue();
1336                     boolean isUnsigned = Boolean.valueOf(t.nextToken()).booleanValue();
1337                     long size = Long.parseLong(t.nextToken());
1338 
1339                     BasicType type = null;
1340                     try {
1341                         type = (BasicType)agent.getTypeDataBase().lookupType(typeName);
1342                     } catch (RuntimeException e) {
1343                     }
1344                     if (type != null) {
1345                         if (type.isOopType() != isOop) {
1346                             throw new RuntimeException("oop mismatch in type definition: " + t.input);
1347                         }
1348                         if (type.isCIntegerType() != isInteger) {
1349                             throw new RuntimeException("integer type mismatch in type definition: " + t.input);
1350                         }
1351                         if (type.isCIntegerType() && (((CIntegerType)type).isUnsigned()) != isUnsigned) {
1352                             throw new RuntimeException("unsigned mismatch in type definition: " + t.input);
1353                         }
1354                         if (type.getSuperclass() == null) {
1355                             if (superclassName != null) {
1356                                 if (type.getSize() == -1) {
1357                                     type.setSuperclass(agent.getTypeDataBase().lookupType(superclassName));
1358                                 } else {
1359                                     throw new RuntimeException("unexpected superclass in type definition: " + t.input);
1360                                 }
1361                             }
1362                         } else {
1363                             if (superclassName == null) {
1364                                 throw new RuntimeException("missing superclass in type definition: " + t.input);
1365                             }
1366                             if (!type.getSuperclass().getName().equals(superclassName)) {
1367                                 throw new RuntimeException("incorrect superclass in type definition: " + t.input);
1368                             }
1369                         }
1370                         if (type.getSize() != size) {
1371                             if (type.getSize() == -1) {
1372                                 type.setSize(size);
1373                             }
1374                             throw new RuntimeException("size mismatch in type definition: " + t.input);
1375                         }
1376                         return;
1377                     }
1378 
1379                     // Create type
1380                     HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
1381                     db.createType(typeName, superclassName, isOop, isInteger, isUnsigned, size);
1382                 } else if (t.countTokens() == 1) {
1383                     Type type = agent.getTypeDataBase().lookupType(t.nextToken());
1384                     dumpType(type);
1385                 } else {
1386                     Iterator i = agent.getTypeDataBase().getTypes();
1387                     // Make sure the types are emitted in an order than can be read back in
1388                     HashSet emitted = new HashSet();
1389                     Stack pending = new Stack();
1390                     while (i.hasNext()) {
1391                         Type n = (Type)i.next();
1392                         if (emitted.contains(n.getName())) {
1393                             continue;
1394                         }
1395 
1396                         while (n != null && !emitted.contains(n.getName())) {
1397                             pending.push(n);
1398                             n = n.getSuperclass();
1399                         }
1400                         while (!pending.empty()) {
1401                             n = (Type)pending.pop();
1402                             dumpType(n);
1403                             emitted.add(n.getName());
1404                         }
1405                     }
1406                 }
1407             }
1408 
1409         },
1410         new Command("source", "source filename", true) {
1411             public void doit(Tokens t) {
1412                 if (t.countTokens() != 1) {
1413                     usage();
1414                     return;
1415                 }
1416                 String file = t.nextToken();
1417                 BufferedReader savedInput = in;
1418                 try {
1419                     BufferedReader input = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
1420                     in = input;
1421                     run(false);
1422                 } catch (Exception e) {
1423                     out.println("Error: " + e);
1424                     if (verboseExceptions) {
1425                         e.printStackTrace(out);
1426                     }
1427                 } finally {
1428                     in = savedInput;
1429                 }
1430 
1431             }
1432         },
1433         new Command("search", "search [ heap | perm | rawheap | codecache | threads ] value", false) {
1434             public void doit(Tokens t) {
1435                 if (t.countTokens() != 2) {
1436                     usage();
1437                     return;
1438                 }
1439                 String type = t.nextToken();
1440                 final Address value = VM.getVM().getDebugger().parseAddress(t.nextToken());
1441                 final long stride = VM.getVM().getAddressSize();
1442                 if (type.equals("threads")) {
1443                     Threads threads = VM.getVM().getThreads();
1444                     for (int i = 0; i < threads.getNumberOfThreads(); i++) {
1445                         JavaThread thread = threads.getJavaThreadAt(i);
1446                         Address base = thread.getStackBase();
1447                         Address end = thread.getLastJavaSP();
1448                         if (end == null) continue;
1449                         if (end.lessThan(base)) {
1450                             Address tmp = base;
1451                             base = end;
1452                             end = tmp;
1453                         }
1454                         //out.println("Searching " + base + " " + end);
1455                         while (base != null && base.lessThan(end)) {
1456                             Address val = base.getAddressAt(0);
1457                             if (AddressOps.equal(val, value)) {
1458                                 ByteArrayOutputStream bos = new ByteArrayOutputStream();
1459                                 thread.printThreadIDOn(new PrintStream(bos));
1460                                 out.println("found on the stack of thread " + bos.toString() + " at " + base);
1461                             }
1462                             base = base.addOffsetTo(stride);
1463                         }
1464                     }
1465                 } else if (type.equals("rawheap")) {
1466                     RawHeapVisitor iterator = new RawHeapVisitor() {
1467                             public void prologue(long used) {
1468                             }
1469 
1470                             public void visitAddress(Address addr) {
1471                                 Address val = addr.getAddressAt(0);
1472                                 if (AddressOps.equal(val, value)) {
1473                                         out.println("found at " + addr);
1474                                 }
1475                             }
1476                             public void visitCompOopAddress(Address addr) {
1477                                 Address val = addr.getCompOopAddressAt(0);
1478                                 if (AddressOps.equal(val, value)) {
1479                                     out.println("found at " + addr);
1480                                 }
1481                             }
1482                             public void epilogue() {
1483                             }
1484                         };
1485                     VM.getVM().getObjectHeap().iterateRaw(iterator);
1486                 } else if (type.equals("heap")) {
1487                     HeapVisitor iterator = new DefaultHeapVisitor() {
1488                             public boolean doObj(Oop obj) {
1489                                 int index = 0;
1490                                 Address start = obj.getHandle();
1491                                 long end = obj.getObjectSize();
1492                                 while (index < end) {
1493                                     Address val = start.getAddressAt(index);
1494                                     if (AddressOps.equal(val, value)) {
1495                                         out.println("found in " + obj.getHandle());
1496                                         break;
1497                                     }
1498                                     index += 4;
1499                                 }
1500                                 return false;
1501                             }
1502                         };
1503                         VM.getVM().getObjectHeap().iterate(iterator);
1504                 } else if (type.equals("codecache")) {
1505                     CodeCacheVisitor v = new CodeCacheVisitor() {
1506                             public void prologue(Address start, Address end) {
1507                             }
1508                             public void visit(CodeBlob blob) {
1509                                 boolean printed = false;
1510                                 Address base = blob.getAddress();
1511                                 Address end = base.addOffsetTo(blob.getSize());
1512                                 while (base != null && base.lessThan(end)) {
1513                                     Address val = base.getAddressAt(0);
1514                                     if (AddressOps.equal(val, value)) {
1515                                         if (!printed) {
1516                                             printed = true;
1517                                             try {
1518                                                 blob.printOn(out);
1519                                             } catch (Exception e) {
1520                                                 out.println("Exception printing blob at " + base);
1521                                                 e.printStackTrace();
1522                                             }
1523                                         }
1524                                         out.println("found at " + base + "\n");
1525                                     }
1526                                     base = base.addOffsetTo(stride);
1527                                 }
1528                             }
1529                             public void epilogue() {
1530                             }
1531 
1532 
1533                         };
1534                     VM.getVM().getCodeCache().iterate(v);
1535 
1536                 }
1537             }
1538         },
1539         new Command("dumpcodecache", "dumpcodecache", false) {
1540             public void doit(Tokens t) {
1541                 if (t.countTokens() != 0) {
1542                     usage();
1543                 } else {
1544                     final PrintStream fout = out;
1545                     final HTMLGenerator gen = new HTMLGenerator(false);
1546                     CodeCacheVisitor v = new CodeCacheVisitor() {
1547                             public void prologue(Address start, Address end) {
1548                             }
1549                             public void visit(CodeBlob blob) {
1550                                 fout.println(gen.genHTML(blob.contentBegin()));
1551                             }
1552                             public void epilogue() {
1553                             }
1554 
1555 
1556                         };
1557                     VM.getVM().getCodeCache().iterate(v);
1558                 }
1559             }
1560         },
1561         new Command("where", "where { -a | id }", false) {
1562             public void doit(Tokens t) {
1563                 if (t.countTokens() != 1) {
1564                     usage();
1565                 } else {
1566                     String name = t.nextToken();
1567                     Threads threads = VM.getVM().getThreads();
1568                     boolean all = name.equals("-a");
1569                     for (int i = 0; i < threads.getNumberOfThreads(); i++) {
1570                         JavaThread thread = threads.getJavaThreadAt(i);
1571                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
1572                         thread.printThreadIDOn(new PrintStream(bos));
1573                         if (all || bos.toString().equals(name)) {
1574                             out.println("Thread " + bos.toString() + " Address: " + thread.getAddress());
1575                             HTMLGenerator gen = new HTMLGenerator(false);
1576                             try {
1577                                 out.println(gen.genHTMLForJavaStackTrace(thread));
1578                             } catch (Exception e) {
1579                                 err.println("Error: " + e);
1580                                 if (verboseExceptions) {
1581                                     e.printStackTrace(err);
1582                                 }
1583                             }
1584                             if (!all) return;
1585                         }
1586                     }
1587                     if (!all) out.println("Couldn't find thread " + name);
1588                 }
1589             }
1590         },
1591         new Command("thread", "thread { -a | id }", false) {
1592             public void doit(Tokens t) {
1593                 if (t.countTokens() != 1) {
1594                     usage();
1595                 } else {
1596                     String name = t.nextToken();
1597                     Threads threads = VM.getVM().getThreads();
1598                     boolean all = name.equals("-a");
1599                     for (int i = 0; i < threads.getNumberOfThreads(); i++) {
1600                         JavaThread thread = threads.getJavaThreadAt(i);
1601                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
1602                         thread.printThreadIDOn(new PrintStream(bos));
1603                         if (all || bos.toString().equals(name)) {
1604                             out.println("Thread " + bos.toString() + " Address " + thread.getAddress());
1605                             thread.printInfoOn(out);
1606                             out.println(" ");
1607                             if (!all) return;
1608                         }
1609                     }
1610                     if (!all) {
1611                         out.println("Couldn't find thread " + name);
1612                     }
1613                 }
1614             }
1615         },
1616 
1617         new Command("threads", false) {
1618             public void doit(Tokens t) {
1619                 if (t.countTokens() != 0) {
1620                     usage();
1621                 } else {
1622                     Threads threads = VM.getVM().getThreads();
1623                     for (int i = 0; i < threads.getNumberOfThreads(); i++) {
1624                         JavaThread thread = threads.getJavaThreadAt(i);
1625                         thread.printThreadIDOn(out);
1626                         out.println(" " + thread.getThreadName());
1627                         thread.printInfoOn(out);
1628                         out.println("\n...");
1629                     }
1630                 }
1631             }
1632         },
1633 
1634         new Command("livenmethods", false) {
1635             public void doit(Tokens t) {
1636                 if (t.countTokens() != 0) {
1637                     usage();
1638                 } else {
1639                     ArrayList nmethods = new ArrayList();
1640                     Threads threads = VM.getVM().getThreads();
1641                     HTMLGenerator gen = new HTMLGenerator(false);
1642                     for (int i = 0; i < threads.getNumberOfThreads(); i++) {
1643                         JavaThread thread = threads.getJavaThreadAt(i);
1644                         try {
1645                             for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) {
1646                                 if (vf instanceof CompiledVFrame) {
1647                                     NMethod c = ((CompiledVFrame)vf).getCode();
1648                                     if (!nmethods.contains(c)) {
1649                                         nmethods.add(c);
1650                                         out.println(gen.genHTML(c));
1651                                     }
1652                                 }
1653                             }
1654                         } catch (Exception e) {
1655                             e.printStackTrace();
1656                         }
1657                     }
1658                 }
1659             }
1660         },
1661         new Command("g1regiondetails", false) {
1662             public void doit(Tokens t) {
1663                 if (t.countTokens() != 0) {
1664                     usage();
1665                 } else {
1666                     CollectedHeap heap = VM.getVM().getUniverse().heap();
1667                     if (!(heap instanceof G1CollectedHeap)) {
1668                         out.println("This command is valid only for G1GC.");
1669                         return;
1670                     }
1671                     out.println("Region Details:");
1672                     ((G1CollectedHeap)heap).printRegionDetails(out);
1673                 }
1674             }
1675         },
1676         new Command("universe", false) {
1677             public void doit(Tokens t) {
1678                 if (t.countTokens() != 0) {
1679                     usage();
1680                 } else {
1681                     Universe u = VM.getVM().getUniverse();
1682                     out.println("Heap Parameters:");
1683                     u.heap().printOn(out);
1684                 }
1685             }
1686         },
1687         new Command("verbose", "verbose true | false", true) {
1688             public void doit(Tokens t) {
1689                 if (t.countTokens() != 1) {
1690                     usage();
1691                 } else {
1692                     verboseExceptions = Boolean.valueOf(t.nextToken()).booleanValue();
1693                 }
1694             }
1695         },
1696         new Command("assert", "assert true | false", true) {
1697             public void doit(Tokens t) {
1698                 if (t.countTokens() != 1) {
1699                     usage();
1700                 } else {
1701                     Assert.ASSERTS_ENABLED = Boolean.valueOf(t.nextToken()).booleanValue();
1702                 }
1703             }
1704         },
1705     };
1706 
1707     private boolean verboseExceptions = false;
1708     private ArrayList history = new ArrayList();
1709     private HashMap commands = new HashMap();
1710     private boolean doEcho = false;
1711 
findCommand(String key)1712     private Command findCommand(String key) {
1713         return (Command)commands.get(key);
1714     }
1715 
printPrompt()1716     public void printPrompt() {
1717         out.print("hsdb> ");
1718     }
1719 
1720     private DebuggerInterface debugger;
1721     private HotSpotAgent agent;
1722     private JSJavaScriptEngine jsengine;
1723     private BufferedReader in;
1724     private PrintStream out;
1725     private PrintStream err;
1726 
1727     // called before debuggee attach
preAttach()1728     private void preAttach() {
1729         // nothing for now..
1730     }
1731 
1732     // called after debuggee attach
postAttach()1733     private void postAttach() {
1734         /*
1735          * JavaScript engine no longer works. For now disable it. Eventually we will remove it.
1736         // create JavaScript engine and start it
1737         try {
1738             jsengine = new JSJavaScriptEngine() {
1739                         private ObjectReader reader = new ObjectReader();
1740                         private JSJavaFactory factory = new JSJavaFactoryImpl();
1741                         public ObjectReader getObjectReader() {
1742                             return reader;
1743                         }
1744                         public JSJavaFactory getJSJavaFactory() {
1745                             return factory;
1746                         }
1747                         protected void quit() {
1748                             debugger.detach();
1749                             quit = true;
1750                         }
1751                         protected BufferedReader getInputReader() {
1752                             return in;
1753                         }
1754                         protected PrintStream getOutputStream() {
1755                             return out;
1756                         }
1757                         protected PrintStream getErrorStream() {
1758                             return err;
1759                         }
1760                    };
1761             try {
1762                 jsengine.defineFunction(this,
1763                      this.getClass().getMethod("registerCommand",
1764                                 new Class[] {
1765                                      String.class, String.class, String.class
1766                                 }));
1767             } catch (NoSuchMethodException exp) {
1768                   // should not happen, see below...!!
1769                   exp.printStackTrace();
1770             }
1771             jsengine.start();
1772         }
1773         catch (Exception ex) {
1774             System.out.println("Warning! JS Engine can't start, some commands will not be available.");
1775             if (verboseExceptions) {
1776                 ex.printStackTrace(out);
1777             }
1778         }
1779         */
1780     }
1781 
registerCommand(String cmd, String usage, final String func)1782     public void registerCommand(String cmd, String usage, final String func) {
1783         commands.put(cmd, new Command(cmd, usage, false) {
1784                               public void doit(Tokens t) {
1785                                   final int len = t.countTokens();
1786                                   Object[] args = new Object[len];
1787                                   for (int i = 0; i < len; i++) {
1788                                       args[i] = t.nextToken();
1789                                   }
1790                                   jsengine.call(func, args);
1791                               }
1792                           });
1793     }
1794 
setOutput(PrintStream o)1795     public void setOutput(PrintStream o) {
1796         out = o;
1797     }
1798 
setErr(PrintStream e)1799     public void setErr(PrintStream e) {
1800         err = e;
1801     }
1802 
CommandProcessor(DebuggerInterface debugger, BufferedReader in, PrintStream out, PrintStream err)1803     public CommandProcessor(DebuggerInterface debugger, BufferedReader in, PrintStream out, PrintStream err) {
1804         this.debugger = debugger;
1805         this.agent = debugger.getAgent();
1806         this.in = in;
1807         this.out = out;
1808         this.err = err;
1809         for (int i = 0; i < commandList.length; i++) {
1810             Command c = commandList[i];
1811             if (commands.get(c.name) != null) {
1812                 throw new InternalError(c.name + " has multiple definitions");
1813             }
1814             commands.put(c.name, c);
1815         }
1816         if (debugger.isAttached()) {
1817             postAttach();
1818         }
1819     }
1820 
1821 
run(boolean prompt)1822     public void run(boolean prompt) {
1823         // Process interactive commands.
1824         while (!quit) {
1825             if (prompt) printPrompt();
1826             String ln = null;
1827             try {
1828                 ln = in.readLine();
1829             } catch (IOException e) {
1830             }
1831             if (ln == null) {
1832                 if (prompt) err.println("Input stream closed.");
1833                 return;
1834             }
1835 
1836             executeCommand(ln, prompt);
1837         }
1838     }
1839 
1840     static Pattern historyPattern = Pattern.compile("((!\\*)|(!\\$)|(!!-?)|(!-?[0-9][0-9]*)|(![a-zA-Z][^ ]*))");
1841 
executeCommand(String ln, boolean putInHistory)1842     public void executeCommand(String ln, boolean putInHistory) {
1843         if (ln.indexOf('!') != -1) {
1844             int size = history.size();
1845             if (size == 0) {
1846                 ln = "";
1847                 err.println("History is empty");
1848             } else {
1849                 StringBuffer result = new StringBuffer();
1850                 Matcher m = historyPattern.matcher(ln);
1851                 int start = 0;
1852                 while (m.find()) {
1853                     if (m.start() > start) {
1854                         result.append(ln.substring(start, m.start() - start));
1855                     }
1856                     start = m.end();
1857 
1858                     String cmd = m.group();
1859                     if (cmd.equals("!!")) {
1860                         result.append((String)history.get(history.size() - 1));
1861                     } else if (cmd.equals("!!-")) {
1862                         Tokens item = new Tokens((String)history.get(history.size() - 1));
1863                         item.trim(1);
1864                         result.append(item.join(" "));
1865                     } else if (cmd.equals("!*")) {
1866                         Tokens item = new Tokens((String)history.get(history.size() - 1));
1867                         item.nextToken();
1868                         result.append(item.join(" "));
1869                     } else if (cmd.equals("!$")) {
1870                         Tokens item = new Tokens((String)history.get(history.size() - 1));
1871                         result.append(item.at(item.countTokens() - 1));
1872                     } else {
1873                         String tail = cmd.substring(1);
1874                         switch (tail.charAt(0)) {
1875                         case '0':
1876                         case '1':
1877                         case '2':
1878                         case '3':
1879                         case '4':
1880                         case '5':
1881                         case '6':
1882                         case '7':
1883                         case '8':
1884                         case '9':
1885                         case '-': {
1886                             int index = Integer.parseInt(tail);
1887                             if (index < 0) {
1888                                 index = history.size() + index;
1889                             }
1890                             if (index > size) {
1891                                 err.println("No such history item");
1892                             } else {
1893                                 result.append((String)history.get(index));
1894                             }
1895                             break;
1896                         }
1897                         default: {
1898                             for (int i = history.size() - 1; i >= 0; i--) {
1899                                 String s = (String)history.get(i);
1900                                 if (s.startsWith(tail)) {
1901                                     result.append(s);
1902                                 }
1903                             }
1904                         }
1905                         }
1906                     }
1907                 }
1908                 if (result.length() == 0) {
1909                     err.println("malformed history reference");
1910                     ln = "";
1911                 } else {
1912                     if (start < ln.length()) {
1913                         result.append(ln.substring(start));
1914                     }
1915                     ln = result.toString();
1916                     if (!doEcho) {
1917                         out.println(ln);
1918                     }
1919                 }
1920             }
1921         }
1922 
1923         if (doEcho) {
1924             out.println("+ " + ln);
1925         }
1926 
1927         PrintStream redirect = null;
1928         Tokens t = new Tokens(ln);
1929         if (t.hasMoreTokens()) {
1930             boolean error = false;
1931             if (putInHistory) history.add(ln);
1932             int len = t.countTokens();
1933             if (len > 2) {
1934                 String r = t.at(len - 2);
1935                 if (r.equals(">") || r.equals(">>")) {
1936                     boolean append = r.length() == 2;
1937                     String file = t.at(len - 1);
1938                     try {
1939                         redirect = new PrintStream(new BufferedOutputStream(new FileOutputStream(file, append)));
1940                         t.trim(2);
1941                     } catch (Exception e) {
1942                         out.println("Error: " + e);
1943                         if (verboseExceptions) {
1944                             e.printStackTrace(out);
1945                         }
1946                         error = true;
1947                     }
1948                 }
1949             }
1950             if (!error) {
1951                 PrintStream savedout = out;
1952                 if (redirect != null) {
1953                     out = redirect;
1954                 }
1955                 try {
1956                     executeCommand(t);
1957                 } catch (Exception e) {
1958                     err.println("Error: " + e);
1959                     if (verboseExceptions) {
1960                         e.printStackTrace(err);
1961                     }
1962                 } finally {
1963                     if (redirect != null) {
1964                         out = savedout;
1965                         redirect.close();
1966                     }
1967                 }
1968             }
1969         }
1970     }
1971 
executeCommand(Tokens args)1972     void executeCommand(Tokens args) {
1973         String cmd = args.nextToken();
1974 
1975         Command doit = findCommand(cmd);
1976 
1977         /*
1978          * Check for an unknown command
1979          */
1980         if (doit == null) {
1981             out.println("Unrecognized command.  Try help...");
1982         } else if (!debugger.isAttached() && !doit.okIfDisconnected) {
1983             out.println("Command not valid until attached to a VM");
1984         } else {
1985             try {
1986                 doit.doit(args);
1987             } catch (Exception e) {
1988                 out.println("Error: " + e);
1989                 if (verboseExceptions) {
1990                     e.printStackTrace(out);
1991                 }
1992             }
1993         }
1994     }
1995 }
1996