1 /*
   2  * Copyright (c) 2005, 2013, 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 with null classloader
 553             // will go in boot.jar and everything else will go in
 554             // app.jar. boot.jar usually not needed, unless changed by jvmti.
 555             public void doit(Tokens t) {
 556                 int tcount = t.countTokens();
 557                 if (tcount > 2) {
 558                     usage();
 559                     return;
 560                 }
 561                 try {
 562                    String prefix = "";
 563                    String option = "all"; // default
 564                    switch(tcount) {
 565                        case 0:
 566                            break;
 567                        case 1:
 568                            option = t.nextToken();
 569                            if (!option.equalsIgnoreCase("all") && !option.equalsIgnoreCase("app") &&
 570                                !option.equalsIgnoreCase("root")) {
 571                               prefix = option;
 572                               option = "all";
 573                            }
 574                            break;
 575                        case 2:
 576                            option = t.nextToken();
 577                            prefix = t.nextToken();
 578                            break;
 579                        default:
 580                            usage();
 581                            return;
 582                    }
 583                    if (!option.equalsIgnoreCase("all") && !option.equalsIgnoreCase("app") &&
 584                                !option.equalsIgnoreCase("boot")) {
 585                        usage();
 586                        return;
 587                    }
 588                    ClassDump cd = new ClassDump();
 589                    if (option.equalsIgnoreCase("all") || option.equalsIgnoreCase("boot")) {
 590                      cd.setClassFilter(new BootFilter());
 591                      cd.setJarOutput(prefix + "boot.jar");
 592                      cd.run();
 593                    }
 594                    if (option.equalsIgnoreCase("all") || option.equalsIgnoreCase("app")) {
 595                      cd.setClassFilter(new NonBootFilter());
 596                      cd.setJarOutput(prefix + "app.jar");
 597                      cd.run();
 598                    }
 599                 } catch (IOException ioe) {
 600                    ioe.printStackTrace();
 601                 }
 602             }
 603         },
 604         new Command("findpc", "findpc address", false) {
 605             public void doit(Tokens t) {
 606                 if (t.countTokens() != 1) {
 607                     usage();
 608                 } else {
 609                     Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
 610                     PointerLocation loc = PointerFinder.find(a);
 611                     loc.printOn(out);
 612                 }
 613             }
 614         },
 615         new Command("symbol", "symbol address", false) {
 616             public void doit(Tokens t) {
 617                 if (t.countTokens() != 1) {
 618                     usage();
 619                 } else {
 620                     Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
 621                     Symbol.create(a).printValueOn(out);
 622                     out.println();
 623                 }
 624             }
 625         },
 626         new Command("symboltable", "symboltable name", false) {
 627             public void doit(Tokens t) {
 628                 if (t.countTokens() != 1) {
 629                     usage();
 630                 } else {
 631                     out.println(SymbolTable.getTheTable().probe(t.nextToken()));
 632                 }
 633             }
 634         },
 635         new Command("symboldump", "symboldump", false) {
 636             public void doit(Tokens t) {
 637                 SymbolTable.getTheTable().symbolsDo(new SymbolTable.SymbolVisitor() {
 638                         public void visit(Symbol sym) {
 639                             sym.printValueOn(out);
 640                             out.println();
 641                         }
 642                     });
 643             }
 644         },
 645         new Command("flags", "flags [ flag | -nd ]", false) {
 646             public void doit(Tokens t) {
 647                 int tokens = t.countTokens();
 648                 if (tokens != 0 && tokens != 1) {
 649                     usage();
 650                 } else {
 651                     String name = tokens > 0 ? t.nextToken() : null;
 652                     boolean nonDefault = false;
 653                     if (name != null && name.equals("-nd")) {
 654                         name = null;
 655                         nonDefault = true;
 656                     }
 657 
 658                     VM.Flag[] flags = VM.getVM().getCommandLineFlags();
 659                     if (flags == null) {
 660                         out.println("Command Flag info not available (use 1.4.1_03 or later)!");
 661                     } else {
 662                         boolean printed = false;
 663                         for (int f = 0; f < flags.length; f++) {
 664                             VM.Flag flag = flags[f];
 665                             if (name == null || flag.getName().equals(name)) {
 666 
 667                                 if (nonDefault && flag.getOrigin() == 0) {
 668                                     // only print flags which aren't their defaults
 669                                     continue;
 670                                 }
 671                                 out.println(flag.getName() + " = " + flag.getValue() + " " + flag.getOrigin());
 672                                 printed = true;
 673                             }
 674                         }
 675                         if (name != null && !printed) {
 676                             out.println("Couldn't find flag: " + name);
 677                         }
 678                     }
 679                 }
 680             }
 681         },
 682         new Command("help", "help [ command ]", true) {
 683             public void doit(Tokens t) {
 684                 int tokens = t.countTokens();
 685                 Command cmd = null;
 686                 if (tokens == 1) {
 687                     cmd = findCommand(t.nextToken());
 688                 }
 689 
 690                 if (cmd != null) {
 691                     cmd.usage();
 692                 } else if (tokens == 0) {
 693                     out.println("Available commands:");
 694                     Object[] keys = commands.keySet().toArray();
 695                     Arrays.sort(keys, new Comparator() {
 696                              public int compare(Object o1, Object o2) {
 697                                  return o1.toString().compareTo(o2.toString());
 698                              }
 699                           });
 700                     for (int i = 0; i < keys.length; i++) {
 701                         out.print("  ");
 702                         out.println(((Command)commands.get(keys[i])).usage);
 703                     }
 704                 }
 705             }
 706         },
 707         new Command("history", "history", true) {
 708             public void doit(Tokens t) {
 709                 int tokens = t.countTokens();
 710                 if (tokens != 0 && (tokens != 1 || !t.nextToken().equals("-h"))) {
 711                     usage();
 712                     return;
 713                 }
 714                 boolean printIndex = tokens == 0;
 715                 for (int i = 0; i < history.size(); i++) {
 716                     if (printIndex) out.print(i + " ");
 717                     out.println(history.get(i));
 718                 }
 719             }
 720         },
 721         // decode raw address
 722         new Command("dis", "dis address [length]", false) {
 723             public void doit(Tokens t) {
 724                 int tokens = t.countTokens();
 725                 if (tokens != 1 && tokens != 2) {
 726                     usage();
 727                     return;
 728                 }
 729                 String name = t.nextToken();
 730                 Address addr = null;
 731                 int len = 0x10; // default length
 732                 try {
 733                     addr = VM.getVM().getDebugger().parseAddress(name);
 734                 } catch (NumberFormatException e) {
 735                    out.println(e);
 736                    return;
 737                 }
 738                 if (tokens == 2) {
 739                     try {
 740                         len = Integer.parseInt(t.nextToken());
 741                     } catch (NumberFormatException e) {
 742                         out.println(e);
 743                         return;
 744                     }
 745                 }
 746                 HTMLGenerator generator = new HTMLGenerator(false);
 747                 out.println(generator.genHTMLForRawDisassembly(addr, len));
 748             }
 749 
 750         },
 751         // decode codeblob or nmethod
 752         new Command("disassemble", "disassemble address", false) {
 753             public void doit(Tokens t) {
 754                 int tokens = t.countTokens();
 755                 if (tokens != 1) {
 756                     usage();
 757                     return;
 758                 }
 759                 String name = t.nextToken();
 760                 Address addr = null;
 761                 try {
 762                     addr = VM.getVM().getDebugger().parseAddress(name);
 763                 } catch (NumberFormatException e) {
 764                    out.println(e);
 765                    return;
 766                 }
 767 
 768                 HTMLGenerator generator = new HTMLGenerator(false);
 769                 out.println(generator.genHTML(addr));
 770             }
 771         },
 772         // print Java bytecode disassembly
 773         new Command("jdis", "jdis address", false) {
 774             public void doit(Tokens t) {
 775                 int tokens = t.countTokens();
 776                 if (tokens != 1) {
 777                     usage();
 778                     return;
 779                 }
 780                 Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
 781                 Method m = (Method)Metadata.instantiateWrapperFor(a);
 782                 HTMLGenerator html = new HTMLGenerator(false);
 783                 out.println(html.genHTML(m));
 784             }
 785         },
 786         new Command("revptrs", "revptrs address", false) {
 787             public void doit(Tokens t) {
 788                 int tokens = t.countTokens();
 789                 if (tokens != 1 && (tokens != 2 || !t.nextToken().equals("-c"))) {
 790                     usage();
 791                     return;
 792                 }
 793                 boolean chase = tokens == 2;
 794                 ReversePtrs revptrs = VM.getVM().getRevPtrs();
 795                 if (revptrs == null) {
 796                     out.println("Computing reverse pointers...");
 797                     ReversePtrsAnalysis analysis = new ReversePtrsAnalysis();
 798                     final boolean[] complete = new boolean[1];
 799                     HeapProgressThunk thunk = new HeapProgressThunk() {
 800                             public void heapIterationFractionUpdate(double d) {}
 801                             public synchronized void heapIterationComplete() {
 802                                 complete[0] = true;
 803                                 notify();
 804                             }
 805                         };
 806                     analysis.setHeapProgressThunk(thunk);
 807                     analysis.run();
 808                     while (!complete[0]) {
 809                         synchronized (thunk) {
 810                             try {
 811                                 thunk.wait();
 812                             } catch (Exception e) {
 813                             }
 814                         }
 815                     }
 816                     revptrs = VM.getVM().getRevPtrs();
 817                     out.println("Done.");
 818                 }
 819                 Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
 820                 if (VM.getVM().getUniverse().heap().isInReserved(a)) {
 821                     OopHandle handle = a.addOffsetToAsOopHandle(0);
 822                     Oop oop = VM.getVM().getObjectHeap().newOop(handle);
 823                     ArrayList ptrs = revptrs.get(oop);
 824                     if (ptrs == null) {
 825                         out.println("no live references to " + a);
 826                     } else {
 827                         if (chase) {
 828                             while (ptrs.size() == 1) {
 829                                 LivenessPathElement e = (LivenessPathElement)ptrs.get(0);
 830                                 ByteArrayOutputStream bos = new ByteArrayOutputStream();
 831                                 Oop.printOopValueOn(e.getObj(), new PrintStream(bos));
 832                                 out.println(bos.toString());
 833                                 ptrs = revptrs.get(e.getObj());
 834                             }
 835                         } else {
 836                             for (int i = 0; i < ptrs.size(); i++) {
 837                                 LivenessPathElement e = (LivenessPathElement)ptrs.get(i);
 838                                 ByteArrayOutputStream bos = new ByteArrayOutputStream();
 839                                 Oop.printOopValueOn(e.getObj(), new PrintStream(bos));
 840                                 out.println(bos.toString());
 841                                 oop = e.getObj();
 842                             }
 843                         }
 844                     }
 845                 }
 846             }
 847         },
 848         new Command("printmdo", "printmdo [ -a | expression ]", false) {
 849             // Print every MDO in the heap or the one referenced by expression.
 850             public void doit(Tokens t) {
 851                 if (t.countTokens() != 1) {
 852                     usage();
 853                 } else {
 854                     String s = t.nextToken();
 855                     if (s.equals("-a")) {
 856                         SystemDictionary sysDict = VM.getVM().getSystemDictionary();
 857                         sysDict.allClassesDo(new SystemDictionary.ClassVisitor() {
 858                                 public void visit(Klass k) {
 859                                     if (k instanceof InstanceKlass) {
 860                                         MethodArray methods = ((InstanceKlass)k).getMethods();
 861                                         for (int i = 0; i < methods.length(); i++) {
 862                                             Method m = methods.at(i);
 863                                             MethodData mdo = m.getMethodData();
 864                                             if (mdo != null) {
 865                                                 out.println("MethodData " + mdo.getAddress() + " for " +
 866                                                     "method " + m.getMethodHolder().getName().asString() + "." +
 867                                                     m.getName().asString() +
 868                                                             m.getSignature().asString() + "@" + m.getAddress());
 869                                                 mdo.printDataOn(out);
 870                                     }
 871                                 }
 872                                     }
 873                                 }
 874                             }
 875                             );
 876                     } else {
 877                         Address a = VM.getVM().getDebugger().parseAddress(s);
 878                         MethodData mdo = (MethodData) Metadata.instantiateWrapperFor(a);
 879                         mdo.printDataOn(out);
 880                     }
 881                 }
 882             }
 883         },
 884         new Command("printall", "printall", false) {
 885             // Print every MDO in the heap or the one referenced by expression.
 886             public void doit(Tokens t) {
 887                 if (t.countTokens() != 0) {
 888                     usage();
 889                 } else {
 890                     SystemDictionary sysDict = VM.getVM().getSystemDictionary();
 891                     sysDict.allClassesDo(new SystemDictionary.ClassVisitor() {
 892                             public void visit(Klass k) {
 893                                 if (k instanceof InstanceKlass && ((InstanceKlass)k).getConstants().getCache() != null) {
 894                                     MethodArray methods = ((InstanceKlass)k).getMethods();
 895                                     for (int i = 0; i < methods.length(); i++) {
 896                                         Method m = methods.at(i);
 897                                         HTMLGenerator gen = new HTMLGenerator(false);
 898                                         out.println(gen.genHTML(m));
 899                                     }
 900                                 }
 901                             }
 902                         }
 903                         );
 904                 }
 905             }
 906         },
 907         new Command("dumpideal", "dumpideal { -a | id }", false) {
 908             // Do a full dump of the nodes reachabile from root in each compiler thread.
 909             public void doit(Tokens t) {
 910                 if (t.countTokens() != 1) {
 911                     usage();
 912                 } else {
 913                     String name = t.nextToken();
 914                     boolean all = name.equals("-a");
 915                     Threads threads = VM.getVM().getThreads();
 916                     for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
 917                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
 918                         thread.printThreadIDOn(new PrintStream(bos));
 919                         if (all || bos.toString().equals(name)) {
 920                           if (thread instanceof CompilerThread) {
 921                             CompilerThread ct = (CompilerThread)thread;
 922                             out.println(ct);
 923                             ciEnv env = ct.env();
 924                             if (env != null) {
 925                               Compile c = env.compilerData();
 926                               c.root().dump(9999, out);
 927                             } else {
 928                               out.println("  not compiling");
 929                             }
 930                           }
 931                         }
 932                     }
 933                 }
 934             }
 935         },
 936         new Command("dumpcfg", "dumpcfg { -a | id }", false) {
 937             // Dump the PhaseCFG for every compiler thread that has one live.
 938             public void doit(Tokens t) {
 939                 if (t.countTokens() != 1) {
 940                     usage();
 941                 } else {
 942                     String name = t.nextToken();
 943                     boolean all = name.equals("-a");
 944                     Threads threads = VM.getVM().getThreads();
 945                     for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
 946                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
 947                         thread.printThreadIDOn(new PrintStream(bos));
 948                         if (all || bos.toString().equals(name)) {
 949                           if (thread instanceof CompilerThread) {
 950                             CompilerThread ct = (CompilerThread)thread;
 951                             out.println(ct);
 952                             ciEnv env = ct.env();
 953                             if (env != null) {
 954                               Compile c = env.compilerData();
 955                               c.cfg().dump(out);
 956                             }
 957                           }
 958                         }
 959                     }
 960                 }
 961             }
 962         },
 963         new Command("dumpilt", "dumpilt { -a | id }", false) {
 964             // dumps the InlineTree of a C2 compile
 965             public void doit(Tokens t) {
 966                 if (t.countTokens() != 1) {
 967                     usage();
 968                 } else {
 969                     String name = t.nextToken();
 970                     boolean all = name.equals("-a");
 971                     Threads threads = VM.getVM().getThreads();
 972                     for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
 973                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
 974                         thread.printThreadIDOn(new PrintStream(bos));
 975                         if (all || bos.toString().equals(name)) {
 976                             if (thread instanceof CompilerThread) {
 977                                 CompilerThread ct = (CompilerThread)thread;
 978                                 ciEnv env = ct.env();
 979                                 if (env != null) {
 980                                     Compile c = env.compilerData();
 981                                     InlineTree ilt = c.ilt();
 982                                     if (ilt != null) {
 983                                         ilt.print(out);
 984                                     }
 985                                 }
 986                             }
 987                         }
 988                     }
 989                 }
 990             }
 991         },
 992         new Command("vmstructsdump", "vmstructsdump", false) {
 993             public void doit(Tokens t) {
 994                 if (t.countTokens() != 0) {
 995                     usage();
 996                     return;
 997                 }
 998 
 999                 // Dump a copy of the type database in a form that can
1000                 // be read back.
1001                 Iterator i = agent.getTypeDataBase().getTypes();
1002                 // Make sure the types are emitted in an order than can be read back in
1003                 HashSet emitted = new HashSet();
1004                 Stack pending = new Stack();
1005                 while (i.hasNext()) {
1006                     Type n = (Type)i.next();
1007                     if (emitted.contains(n.getName())) {
1008                         continue;
1009                     }
1010 
1011                     while (n != null && !emitted.contains(n.getName())) {
1012                         pending.push(n);
1013                         n = n.getSuperclass();
1014                     }
1015                     while (!pending.empty()) {
1016                         n = (Type)pending.pop();
1017                         dumpType(n);
1018                         emitted.add(n.getName());
1019                     }
1020                 }
1021                 i = agent.getTypeDataBase().getTypes();
1022                 while (i.hasNext()) {
1023                     dumpFields((Type)i.next(), false);
1024                 }
1025             }
1026         },
1027 
1028         new Command("inspect", "inspect expression", false) {
1029             public void doit(Tokens t) {
1030                 if (t.countTokens() != 1) {
1031                     usage();
1032                 } else {
1033                     Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
1034                     SimpleTreeNode node = null;
1035                     if (VM.getVM().getUniverse().heap().isInReserved(a)) {
1036                         OopHandle handle = a.addOffsetToAsOopHandle(0);
1037                         Oop oop = VM.getVM().getObjectHeap().newOop(handle);
1038                         node = new OopTreeNodeAdapter(oop, null);
1039 
1040                         out.println("instance of " + node.getValue() + " @ " + a +
1041                                     " (size = " + oop.getObjectSize() + ")");
1042                     } else if (VM.getVM().getCodeCache().contains(a)) {
1043                         CodeBlob blob = VM.getVM().getCodeCache().findBlobUnsafe(a);
1044                         a = blob.headerBegin();
1045                     }
1046                     if (node == null) {
1047                         Type type = VM.getVM().getTypeDataBase().guessTypeForAddress(a);
1048                         if (type != null) {
1049                             out.println("Type is " + type.getName() + " (size of " + type.getSize() + ")");
1050                             node = new CTypeTreeNodeAdapter(a, type, null);
1051                         }
1052                     }
1053                     if (node != null) {
1054                         printNode(node);
1055                     }
1056                 }
1057             }
1058         },
1059         new Command("jhisto", "jhisto", false) {
1060             public void doit(Tokens t) {
1061                  ObjectHistogram histo = new ObjectHistogram();
1062                  histo.run(out, err);
1063             }
1064         },
1065         new Command("jstack", "jstack [-v]", false) {
1066             public void doit(Tokens t) {
1067                 boolean verbose = false;
1068                 if (t.countTokens() > 0 && t.nextToken().equals("-v")) {
1069                     verbose = true;
1070                 }
1071                 StackTrace jstack = new StackTrace(verbose, true);
1072                 jstack.run(out);
1073             }
1074         },
1075         new Command("print", "print expression", false) {
1076             public void doit(Tokens t) {
1077                 if (t.countTokens() != 1) {
1078                     usage();
1079                 } else {
1080                     Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
1081                     HTMLGenerator gen = new HTMLGenerator(false);
1082                     out.println(gen.genHTML(a));
1083                 }
1084             }
1085         },
1086         new Command("printas", "printas type expression", false) {
1087             public void doit(Tokens t) {
1088                 if (t.countTokens() != 2) {
1089                     usage();
1090                 } else {
1091                     Type type = agent.getTypeDataBase().lookupType(t.nextToken());
1092                     Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
1093                     CTypeTreeNodeAdapter node = new CTypeTreeNodeAdapter(a, type, null);
1094 
1095                     out.println("pointer to " + type + " @ " + a +
1096                                 " (size = " + type.getSize() + ")");
1097                     printNode(node);
1098                 }
1099             }
1100         },
1101         new Command("printstatics", "printstatics [ type ]", false) {
1102             public void doit(Tokens t) {
1103                 if (t.countTokens() > 1) {
1104                     usage();
1105                 } else {
1106                     if (t.countTokens() == 0) {
1107                         out.println("All known static fields");
1108                         printNode(new CTypeTreeNodeAdapter(agent.getTypeDataBase().getTypes()));
1109                     } else {
1110                         Type type = agent.getTypeDataBase().lookupType(t.nextToken());
1111                         out.println("Static fields of " + type.getName());
1112                         printNode(new CTypeTreeNodeAdapter(type));
1113                     }
1114                 }
1115             }
1116         },
1117         new Command("pmap", "pmap", false) {
1118             public void doit(Tokens t) {
1119                 PMap pmap = new PMap();
1120                 pmap.run(out, debugger.getAgent().getDebugger());
1121             }
1122         },
1123         new Command("pstack", "pstack [-v]", false) {
1124             public void doit(Tokens t) {
1125                 boolean verbose = false;
1126                 if (t.countTokens() > 0 && t.nextToken().equals("-v")) {
1127                     verbose = true;
1128                 }
1129                 PStack pstack = new PStack(verbose, true);
1130                 pstack.run(out, debugger.getAgent().getDebugger());
1131             }
1132         },
1133         new Command("quit", true) {
1134             public void doit(Tokens t) {
1135                 if (t.countTokens() != 0) {
1136                     usage();
1137                 } else {
1138                     debugger.detach();
1139                     quit = true;
1140                 }
1141             }
1142         },
1143         new Command("echo", "echo [ true | false ]", true) {
1144             public void doit(Tokens t) {
1145                 if (t.countTokens() == 0) {
1146                     out.println("echo is " + doEcho);
1147                 } else if (t.countTokens() == 1) {
1148                     doEcho = Boolean.valueOf(t.nextToken()).booleanValue();
1149                 } else {
1150                     usage();
1151                 }
1152             }
1153         },
1154         new Command("versioncheck", "versioncheck [ true | false ]", true) {
1155             public void doit(Tokens t) {
1156                 if (t.countTokens() == 0) {
1157                     out.println("versioncheck is " +
1158                                 (System.getProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck") == null));
1159                 } else if (t.countTokens() == 1) {
1160                     if (Boolean.valueOf(t.nextToken()).booleanValue()) {
1161                         System.setProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck", null);
1162                     } else {
1163                         System.setProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck", "true");
1164                     }
1165                 } else {
1166                     usage();
1167                 }
1168             }
1169         },
1170         new Command("scanoops", "scanoops start end [ type ]", false) {
1171             public void doit(Tokens t) {
1172                 if (t.countTokens() != 2 && t.countTokens() != 3) {
1173                     usage();
1174                 } else {
1175                     long stride = VM.getVM().getAddressSize();
1176                     Address base = VM.getVM().getDebugger().parseAddress(t.nextToken());
1177                     Address end  = VM.getVM().getDebugger().parseAddress(t.nextToken());
1178                     Klass klass = null;
1179                     if (t.countTokens() == 1) {
1180                         klass = SystemDictionaryHelper.findInstanceKlass(t.nextToken());
1181                         if (klass == null) {
1182                             out.println("No such type.");
1183                             return;
1184                         }
1185                     }
1186                     while (base != null && base.lessThan(end)) {
1187                         long step = stride;
1188                         OopHandle handle = base.addOffsetToAsOopHandle(0);
1189                         if (RobustOopDeterminator.oopLooksValid(handle)) {
1190                             try {
1191                                 Oop oop = VM.getVM().getObjectHeap().newOop(handle);
1192                                 if (klass == null || oop.getKlass().isSubtypeOf(klass))
1193                                     out.println(handle.toString() + " " + oop.getKlass().getName().asString());
1194                                 step = oop.getObjectSize();
1195                             } catch (UnknownOopException ex) {
1196                                 // ok
1197                             } catch (RuntimeException ex) {
1198                                 ex.printStackTrace();
1199                             }
1200                         }
1201                         base = base.addOffsetTo(step);
1202                     }
1203                 }
1204             }
1205         },
1206         new Command("intConstant", "intConstant [ name [ value ] ]", true) {
1207             public void doit(Tokens t) {
1208                 if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 2) {
1209                     usage();
1210                     return;
1211                 }
1212                 HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
1213                 if (t.countTokens() == 1) {
1214                     String name = t.nextToken();
1215                     out.println("intConstant " + name + " " + db.lookupIntConstant(name));
1216                 } else if (t.countTokens() == 0) {
1217                     Iterator i = db.getIntConstants();
1218                     while (i.hasNext()) {
1219                         String name = (String)i.next();
1220                         out.println("intConstant " + name + " " + db.lookupIntConstant(name));
1221                     }
1222                 } else if (t.countTokens() == 2) {
1223                     String name = t.nextToken();
1224                     Integer value = Integer.valueOf(t.nextToken());
1225                     db.addIntConstant(name, value);
1226                 }
1227             }
1228         },
1229         new Command("longConstant", "longConstant [ name [ value ] ]", true) {
1230             public void doit(Tokens t) {
1231                 if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 2) {
1232                     usage();
1233                     return;
1234                 }
1235                 HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
1236                 if (t.countTokens() == 1) {
1237                     String name = t.nextToken();
1238                     out.println("longConstant " + name + " " + db.lookupLongConstant(name));
1239                 } else if (t.countTokens() == 0) {
1240                     Iterator i = db.getLongConstants();
1241                     while (i.hasNext()) {
1242                         String name = (String)i.next();
1243                         out.println("longConstant " + name + " " + db.lookupLongConstant(name));
1244                     }
1245                 } else if (t.countTokens() == 2) {
1246                     String name = t.nextToken();
1247                     Long value = Long.valueOf(t.nextToken());
1248                     db.addLongConstant(name, value);
1249                 }
1250             }
1251         },
1252         new Command("field", "field [ type [ name fieldtype isStatic offset address ] ]", true) {
1253             public void doit(Tokens t) {
1254                 if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 6) {
1255                     usage();
1256                     return;
1257                 }
1258                 if (t.countTokens() == 1) {
1259                     Type type = agent.getTypeDataBase().lookupType(t.nextToken());
1260                     dumpFields(type);
1261                 } else if (t.countTokens() == 0) {
1262                     Iterator i = agent.getTypeDataBase().getTypes();
1263                     while (i.hasNext()) {
1264                         dumpFields((Type)i.next());
1265                     }
1266                 } else {
1267                     BasicType containingType = (BasicType)agent.getTypeDataBase().lookupType(t.nextToken());
1268 
1269                     String fieldName = t.nextToken();
1270 
1271                     // The field's Type must already be in the database -- no exceptions
1272                     Type fieldType = agent.getTypeDataBase().lookupType(t.nextToken());
1273 
1274                     boolean isStatic = Boolean.valueOf(t.nextToken()).booleanValue();
1275                     long offset = Long.parseLong(t.nextToken());
1276                     Address staticAddress = parseAddress(t.nextToken());
1277                     if (isStatic && staticAddress == null) {
1278                         staticAddress = lookup(containingType.getName() + "::" + fieldName);
1279                     }
1280 
1281                     // check to see if the field already exists
1282                     Iterator i = containingType.getFields();
1283                     while (i.hasNext()) {
1284                         Field f = (Field) i.next();
1285                         if (f.getName().equals(fieldName)) {
1286                             if (f.isStatic() != isStatic) {
1287                                 throw new RuntimeException("static/nonstatic mismatch: " + t.input);
1288                             }
1289                             if (!isStatic) {
1290                                 if (f.getOffset() != offset) {
1291                                     throw new RuntimeException("bad redefinition of field offset: " + t.input);
1292                                 }
1293                             } else {
1294                                 if (!f.getStaticFieldAddress().equals(staticAddress)) {
1295                                     throw new RuntimeException("bad redefinition of field location: " + t.input);
1296                                 }
1297                             }
1298                             if (f.getType() != fieldType) {
1299                                 throw new RuntimeException("bad redefinition of field type: " + t.input);
1300                             }
1301                             return;
1302                         }
1303                     }
1304 
1305                     // Create field by type
1306                     HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
1307                     db.createField(containingType,
1308                                    fieldName, fieldType,
1309                                    isStatic,
1310                                    offset,
1311                                    staticAddress);
1312 
1313                 }
1314             }
1315 
1316         },
1317         new Command("tokenize", "tokenize ...", true) {
1318             public void doit(Tokens t) {
1319                 while (t.hasMoreTokens()) {
1320                     out.println("\"" + t.nextToken() + "\"");
1321                 }
1322             }
1323         },
1324         new Command("type", "type [ type [ name super isOop isInteger isUnsigned size ] ]", true) {
1325             public void doit(Tokens t) {
1326                 if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 6) {
1327                     usage();
1328                     return;
1329                 }
1330                 if (t.countTokens() == 6) {
1331                     String typeName = t.nextToken();
1332                     String superclassName = t.nextToken();
1333                     if (superclassName.equals("null")) {
1334                         superclassName = null;
1335                     }
1336                     boolean isOop = Boolean.valueOf(t.nextToken()).booleanValue();
1337                     boolean isInteger = Boolean.valueOf(t.nextToken()).booleanValue();
1338                     boolean isUnsigned = Boolean.valueOf(t.nextToken()).booleanValue();
1339                     long size = Long.parseLong(t.nextToken());
1340 
1341                     BasicType type = null;
1342                     try {
1343                         type = (BasicType)agent.getTypeDataBase().lookupType(typeName);
1344                     } catch (RuntimeException e) {
1345                     }
1346                     if (type != null) {
1347                         if (type.isOopType() != isOop) {
1348                             throw new RuntimeException("oop mismatch in type definition: " + t.input);
1349                         }
1350                         if (type.isCIntegerType() != isInteger) {
1351                             throw new RuntimeException("integer type mismatch in type definition: " + t.input);
1352                         }
1353                         if (type.isCIntegerType() && (((CIntegerType)type).isUnsigned()) != isUnsigned) {
1354                             throw new RuntimeException("unsigned mismatch in type definition: " + t.input);
1355                         }
1356                         if (type.getSuperclass() == null) {
1357                             if (superclassName != null) {
1358                                 if (type.getSize() == -1) {
1359                                     type.setSuperclass(agent.getTypeDataBase().lookupType(superclassName));
1360                                 } else {
1361                                     throw new RuntimeException("unexpected superclass in type definition: " + t.input);
1362                                 }
1363                             }
1364                         } else {
1365                             if (superclassName == null) {
1366                                 throw new RuntimeException("missing superclass in type definition: " + t.input);
1367                             }
1368                             if (!type.getSuperclass().getName().equals(superclassName)) {
1369                                 throw new RuntimeException("incorrect superclass in type definition: " + t.input);
1370                             }
1371                         }
1372                         if (type.getSize() != size) {
1373                             if (type.getSize() == -1) {
1374                                 type.setSize(size);
1375                             }
1376                             throw new RuntimeException("size mismatch in type definition: " + t.input);
1377                         }
1378                         return;
1379                     }
1380 
1381                     // Create type
1382                     HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
1383                     db.createType(typeName, superclassName, isOop, isInteger, isUnsigned, size);
1384                 } else if (t.countTokens() == 1) {
1385                     Type type = agent.getTypeDataBase().lookupType(t.nextToken());
1386                     dumpType(type);
1387                 } else {
1388                     Iterator i = agent.getTypeDataBase().getTypes();
1389                     // Make sure the types are emitted in an order than can be read back in
1390                     HashSet emitted = new HashSet();
1391                     Stack pending = new Stack();
1392                     while (i.hasNext()) {
1393                         Type n = (Type)i.next();
1394                         if (emitted.contains(n.getName())) {
1395                             continue;
1396                         }
1397 
1398                         while (n != null && !emitted.contains(n.getName())) {
1399                             pending.push(n);
1400                             n = n.getSuperclass();
1401                         }
1402                         while (!pending.empty()) {
1403                             n = (Type)pending.pop();
1404                             dumpType(n);
1405                             emitted.add(n.getName());
1406                         }
1407                     }
1408                 }
1409             }
1410 
1411         },
1412         new Command("source", "source filename", true) {
1413             public void doit(Tokens t) {
1414                 if (t.countTokens() != 1) {
1415                     usage();
1416                     return;
1417                 }
1418                 String file = t.nextToken();
1419                 BufferedReader savedInput = in;
1420                 try {
1421                     BufferedReader input = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
1422                     in = input;
1423                     run(false);
1424                 } catch (Exception e) {
1425                     out.println("Error: " + e);
1426                     if (verboseExceptions) {
1427                         e.printStackTrace(out);
1428                     }
1429                 } finally {
1430                     in = savedInput;
1431                 }
1432 
1433             }
1434         },
1435         new Command("search", "search [ heap | perm | rawheap | codecache | threads ] value", false) {
1436             public void doit(Tokens t) {
1437                 if (t.countTokens() != 2) {
1438                     usage();
1439                     return;
1440                 }
1441                 String type = t.nextToken();
1442                 final Address value = VM.getVM().getDebugger().parseAddress(t.nextToken());
1443                 final long stride = VM.getVM().getAddressSize();
1444                 if (type.equals("threads")) {
1445                     Threads threads = VM.getVM().getThreads();
1446                     for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
1447                         Address base = thread.getStackBase();
1448                         Address end = thread.getLastJavaSP();
1449                         if (end == null) continue;
1450                         if (end.lessThan(base)) {
1451                             Address tmp = base;
1452                             base = end;
1453                             end = tmp;
1454                         }
1455                         //out.println("Searching " + base + " " + end);
1456                         while (base != null && base.lessThan(end)) {
1457                             Address val = base.getAddressAt(0);
1458                             if (AddressOps.equal(val, value)) {
1459                                 ByteArrayOutputStream bos = new ByteArrayOutputStream();
1460                                 thread.printThreadIDOn(new PrintStream(bos));
1461                                 out.println("found on the stack of thread " + bos.toString() + " at " + 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                             thread.printInfoOn(out);
1605                             out.println(" ");
1606                             if (!all) return;
1607                         }
1608                     }
1609                     out.println("Couldn't find thread " + name);
1610                 }
1611             }
1612         },
1613 
1614         new Command("threads", false) {
1615             public void doit(Tokens t) {
1616                 if (t.countTokens() != 0) {
1617                     usage();
1618                 } else {
1619                     Threads threads = VM.getVM().getThreads();
1620                     for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
1621                         thread.printThreadIDOn(out);
1622                         out.println(" " + thread.getThreadName());
1623                         thread.printInfoOn(out);
1624                         out.println("\n...");
1625                     }
1626                 }
1627             }
1628         },
1629 
1630         new Command("livenmethods", false) {
1631             public void doit(Tokens t) {
1632                 if (t.countTokens() != 0) {
1633                     usage();
1634                 } else {
1635                     ArrayList nmethods = new ArrayList();
1636                     Threads threads = VM.getVM().getThreads();
1637                     HTMLGenerator gen = new HTMLGenerator(false);
1638                     for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
1639                         try {
1640                             for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) {
1641                                 if (vf instanceof CompiledVFrame) {
1642                                     NMethod c = ((CompiledVFrame)vf).getCode();
1643                                     if (!nmethods.contains(c)) {
1644                                         nmethods.add(c);
1645                                         out.println(gen.genHTML(c));
1646                                     }
1647                                 }
1648                             }
1649                         } catch (Exception e) {
1650                             e.printStackTrace();
1651                         }
1652                     }
1653                 }
1654             }
1655         },
1656         new Command("universe", false) {
1657             public void doit(Tokens t) {
1658                 if (t.countTokens() != 0) {
1659                     usage();
1660                 } else {
1661                     Universe u = VM.getVM().getUniverse();
1662                     out.println("Heap Parameters:");
1663                     u.heap().printOn(out);
1664                 }
1665             }
1666         },
1667         new Command("verbose", "verbose true | false", true) {
1668             public void doit(Tokens t) {
1669                 if (t.countTokens() != 1) {
1670                     usage();
1671                 } else {
1672                     verboseExceptions = Boolean.valueOf(t.nextToken()).booleanValue();
1673                 }
1674             }
1675         },
1676         new Command("assert", "assert true | false", true) {
1677             public void doit(Tokens t) {
1678                 if (t.countTokens() != 1) {
1679                     usage();
1680                 } else {
1681                     Assert.ASSERTS_ENABLED = Boolean.valueOf(t.nextToken()).booleanValue();
1682                 }
1683             }
1684         },
1685     };
1686 
1687     private boolean verboseExceptions = false;
1688     private ArrayList history = new ArrayList();
1689     private HashMap commands = new HashMap();
1690     private boolean doEcho = false;
1691 
1692     private Command findCommand(String key) {
1693         return (Command)commands.get(key);
1694     }
1695 
1696     public void printPrompt() {
1697         out.print("hsdb> ");
1698     }
1699 
1700     private DebuggerInterface debugger;
1701     private HotSpotAgent agent;
1702     private JSJavaScriptEngine jsengine;
1703     private BufferedReader in;
1704     private PrintStream out;
1705     private PrintStream err;
1706 
1707     // called before debuggee attach
1708     private void preAttach() {
1709         // nothing for now..
1710     }
1711 
1712     // called after debuggee attach
1713     private void postAttach() {
1714         // create JavaScript engine and start it
1715         try {
1716             jsengine = new JSJavaScriptEngine() {
1717                         private ObjectReader reader = new ObjectReader();
1718                         private JSJavaFactory factory = new JSJavaFactoryImpl();
1719                         public ObjectReader getObjectReader() {
1720                             return reader;
1721                         }
1722                         public JSJavaFactory getJSJavaFactory() {
1723                             return factory;
1724                         }
1725                         protected void quit() {
1726                             debugger.detach();
1727                             quit = true;
1728                         }
1729                         protected BufferedReader getInputReader() {
1730                             return in;
1731                         }
1732                         protected PrintStream getOutputStream() {
1733                             return out;
1734                         }
1735                         protected PrintStream getErrorStream() {
1736                             return err;
1737                         }
1738                    };
1739             try {
1740                 jsengine.defineFunction(this,
1741                      this.getClass().getMethod("registerCommand",
1742                                 new Class[] {
1743                                      String.class, String.class, String.class
1744                                 }));
1745             } catch (NoSuchMethodException exp) {
1746                   // should not happen, see below...!!
1747                   exp.printStackTrace();
1748             }
1749             jsengine.start();
1750         }
1751         catch (Exception ex) {
1752             System.out.println("Warning! JS Engine can't start, some commands will not be available.");
1753             if (verboseExceptions) {
1754                 ex.printStackTrace(out);
1755             }
1756         }
1757     }
1758 
1759     public void registerCommand(String cmd, String usage, final String func) {
1760         commands.put(cmd, new Command(cmd, usage, false) {
1761                               public void doit(Tokens t) {
1762                                   final int len = t.countTokens();
1763                                   Object[] args = new Object[len];
1764                                   for (int i = 0; i < len; i++) {
1765                                       args[i] = t.nextToken();
1766                                   }
1767                                   jsengine.call(func, args);
1768                               }
1769                           });
1770     }
1771 
1772     public void setOutput(PrintStream o) {
1773         out = o;
1774     }
1775 
1776     public void setErr(PrintStream e) {
1777         err = e;
1778     }
1779 
1780     public CommandProcessor(DebuggerInterface debugger, BufferedReader in, PrintStream out, PrintStream err) {
1781         this.debugger = debugger;
1782         this.agent = debugger.getAgent();
1783         this.in = in;
1784         this.out = out;
1785         this.err = err;
1786         for (int i = 0; i < commandList.length; i++) {
1787             Command c = commandList[i];
1788             if (commands.get(c.name) != null) {
1789                 throw new InternalError(c.name + " has multiple definitions");
1790             }
1791             commands.put(c.name, c);
1792         }
1793         if (debugger.isAttached()) {
1794             postAttach();
1795         }
1796     }
1797 
1798 
1799     public void run(boolean prompt) {
1800         // Process interactive commands.
1801         while (!quit) {
1802             if (prompt) printPrompt();
1803             String ln = null;
1804             try {
1805                 ln = in.readLine();
1806             } catch (IOException e) {
1807             }
1808             if (ln == null) {
1809                 if (prompt) err.println("Input stream closed.");
1810                 return;
1811             }
1812 
1813             executeCommand(ln, prompt);
1814         }
1815     }
1816 
1817     static Pattern historyPattern = Pattern.compile("((!\\*)|(!\\$)|(!!-?)|(!-?[0-9][0-9]*)|(![a-zA-Z][^ ]*))");
1818 
1819     public void executeCommand(String ln, boolean putInHistory) {
1820         if (ln.indexOf('!') != -1) {
1821             int size = history.size();
1822             if (size == 0) {
1823                 ln = "";
1824                 err.println("History is empty");
1825             } else {
1826                 StringBuffer result = new StringBuffer();
1827                 Matcher m = historyPattern.matcher(ln);
1828                 int start = 0;
1829                 while (m.find()) {
1830                     if (m.start() > start) {
1831                         result.append(ln.substring(start, m.start() - start));
1832                     }
1833                     start = m.end();
1834 
1835                     String cmd = m.group();
1836                     if (cmd.equals("!!")) {
1837                         result.append((String)history.get(history.size() - 1));
1838                     } else if (cmd.equals("!!-")) {
1839                         Tokens item = new Tokens((String)history.get(history.size() - 1));
1840                         item.trim(1);
1841                         result.append(item.join(" "));
1842                     } else if (cmd.equals("!*")) {
1843                         Tokens item = new Tokens((String)history.get(history.size() - 1));
1844                         item.nextToken();
1845                         result.append(item.join(" "));
1846                     } else if (cmd.equals("!$")) {
1847                         Tokens item = new Tokens((String)history.get(history.size() - 1));
1848                         result.append(item.at(item.countTokens() - 1));
1849                     } else {
1850                         String tail = cmd.substring(1);
1851                         switch (tail.charAt(0)) {
1852                         case '0':
1853                         case '1':
1854                         case '2':
1855                         case '3':
1856                         case '4':
1857                         case '5':
1858                         case '6':
1859                         case '7':
1860                         case '8':
1861                         case '9':
1862                         case '-': {
1863                             int index = Integer.parseInt(tail);
1864                             if (index < 0) {
1865                                 index = history.size() + index;
1866                             }
1867                             if (index > size) {
1868                                 err.println("No such history item");
1869                             } else {
1870                                 result.append((String)history.get(index));
1871                             }
1872                             break;
1873                         }
1874                         default: {
1875                             for (int i = history.size() - 1; i >= 0; i--) {
1876                                 String s = (String)history.get(i);
1877                                 if (s.startsWith(tail)) {
1878                                     result.append(s);
1879                                 }
1880                             }
1881                         }
1882                         }
1883                     }
1884                 }
1885                 if (result.length() == 0) {
1886                     err.println("malformed history reference");
1887                     ln = "";
1888                 } else {
1889                     if (start < ln.length()) {
1890                         result.append(ln.substring(start));
1891                     }
1892                     ln = result.toString();
1893                     if (!doEcho) {
1894                         out.println(ln);
1895                     }
1896                 }
1897             }
1898         }
1899 
1900         if (doEcho) {
1901             out.println("+ " + ln);
1902         }
1903 
1904         PrintStream redirect = null;
1905         Tokens t = new Tokens(ln);
1906         if (t.hasMoreTokens()) {
1907             boolean error = false;
1908             if (putInHistory) history.add(ln);
1909             int len = t.countTokens();
1910             if (len > 2) {
1911                 String r = t.at(len - 2);
1912                 if (r.equals(">") || r.equals(">>")) {
1913                     boolean append = r.length() == 2;
1914                     String file = t.at(len - 1);
1915                     try {
1916                         redirect = new PrintStream(new BufferedOutputStream(new FileOutputStream(file, append)));
1917                         t.trim(2);
1918                     } catch (Exception e) {
1919                         out.println("Error: " + e);
1920                         if (verboseExceptions) {
1921                             e.printStackTrace(out);
1922                         }
1923                         error = true;
1924                     }
1925                 }
1926             }
1927             if (!error) {
1928                 PrintStream savedout = out;
1929                 if (redirect != null) {
1930                     out = redirect;
1931                 }
1932                 try {
1933                     executeCommand(t);
1934                 } catch (Exception e) {
1935                     err.println("Error: " + e);
1936                     if (verboseExceptions) {
1937                         e.printStackTrace(err);
1938                     }
1939                 } finally {
1940                     if (redirect != null) {
1941                         out = savedout;
1942                         redirect.close();
1943                     }
1944                 }
1945             }
1946         }
1947     }
1948 
1949     void executeCommand(Tokens args) {
1950         String cmd = args.nextToken();
1951 
1952         Command doit = findCommand(cmd);
1953 
1954         /*
1955          * Check for an unknown command
1956          */
1957         if (doit == null) {
1958             out.println("Unrecognized command.  Try help...");
1959         } else if (!debugger.isAttached() && !doit.okIfDisconnected) {
1960             out.println("Command not valid until attached to a VM");
1961         } else {
1962             try {
1963                 doit.doit(args);
1964             } catch (Exception e) {
1965                 out.println("Error: " + e);
1966                 if (verboseExceptions) {
1967                     e.printStackTrace(out);
1968                 }
1969             }
1970         }
1971     }
1972 }