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