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