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