1 /*
   2  * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 package sun.jvm.hotspot;
  26 
  27 import java.io.BufferedOutputStream;
  28 import java.io.BufferedReader;
  29 import java.io.ByteArrayOutputStream;
  30 import java.io.FileInputStream;
  31 import java.io.FileOutputStream;
  32 import java.io.IOException;
  33 import java.io.InputStreamReader;
  34 import java.io.PrintStream;
  35 import java.util.ArrayList;
  36 import java.util.Arrays;
  37 import java.util.Comparator;
  38 import java.util.HashMap;
  39 import java.util.HashSet;
  40 import java.util.Iterator;
  41 import java.util.Stack;
  42 import java.util.regex.Matcher;
  43 import java.util.regex.Pattern;
  44 
  45 import sun.jvm.hotspot.ci.ciEnv;
  46 import sun.jvm.hotspot.code.CodeBlob;
  47 import sun.jvm.hotspot.code.CodeCacheVisitor;
  48 import sun.jvm.hotspot.code.NMethod;
  49 import sun.jvm.hotspot.debugger.Address;
  50 import sun.jvm.hotspot.debugger.OopHandle;
  51 import sun.jvm.hotspot.classfile.ClassLoaderDataGraph;
  52 import sun.jvm.hotspot.memory.FileMapInfo;
  53 import sun.jvm.hotspot.memory.SystemDictionary;
  54 import sun.jvm.hotspot.memory.Universe;
  55 import sun.jvm.hotspot.gc.shared.CollectedHeap;
  56 import sun.jvm.hotspot.gc.g1.G1CollectedHeap;
  57 import sun.jvm.hotspot.oops.DefaultHeapVisitor;
  58 import sun.jvm.hotspot.oops.HeapVisitor;
  59 import sun.jvm.hotspot.oops.InstanceKlass;
  60 import sun.jvm.hotspot.oops.Klass;
  61 import sun.jvm.hotspot.oops.Metadata;
  62 import sun.jvm.hotspot.oops.Method;
  63 import sun.jvm.hotspot.oops.MethodData;
  64 import sun.jvm.hotspot.oops.Oop;
  65 import sun.jvm.hotspot.oops.RawHeapVisitor;
  66 import sun.jvm.hotspot.oops.Symbol;
  67 import sun.jvm.hotspot.oops.UnknownOopException;
  68 import sun.jvm.hotspot.opto.Compile;
  69 import sun.jvm.hotspot.opto.InlineTree;
  70 import sun.jvm.hotspot.runtime.CompiledVFrame;
  71 import sun.jvm.hotspot.runtime.CompilerThread;
  72 import sun.jvm.hotspot.runtime.JavaThread;
  73 import sun.jvm.hotspot.runtime.JavaVFrame;
  74 import sun.jvm.hotspot.runtime.Threads;
  75 import sun.jvm.hotspot.runtime.VM;
  76 import sun.jvm.hotspot.tools.ObjectHistogram;
  77 import sun.jvm.hotspot.tools.PMap;
  78 import sun.jvm.hotspot.tools.PStack;
  79 import sun.jvm.hotspot.tools.StackTrace;
  80 import sun.jvm.hotspot.tools.jcore.ClassDump;
  81 import sun.jvm.hotspot.tools.jcore.ClassFilter;
  82 import sun.jvm.hotspot.types.CIntegerType;
  83 import sun.jvm.hotspot.types.Field;
  84 import sun.jvm.hotspot.types.Type;
  85 import sun.jvm.hotspot.types.basic.BasicType;
  86 import sun.jvm.hotspot.ui.classbrowser.HTMLGenerator;
  87 import sun.jvm.hotspot.ui.tree.CTypeTreeNodeAdapter;
  88 import sun.jvm.hotspot.ui.tree.OopTreeNodeAdapter;
  89 import sun.jvm.hotspot.ui.tree.SimpleTreeNode;
  90 import sun.jvm.hotspot.utilities.AddressOps;
  91 import sun.jvm.hotspot.utilities.Assert;
  92 import sun.jvm.hotspot.utilities.CompactHashTable;
  93 import sun.jvm.hotspot.utilities.HeapProgressThunk;
  94 import sun.jvm.hotspot.utilities.LivenessPathElement;
  95 import sun.jvm.hotspot.utilities.MethodArray;
  96 import sun.jvm.hotspot.utilities.ObjectReader;
  97 import sun.jvm.hotspot.utilities.PointerFinder;
  98 import sun.jvm.hotspot.utilities.PointerLocation;
  99 import sun.jvm.hotspot.utilities.ReversePtrs;
 100 import sun.jvm.hotspot.utilities.ReversePtrsAnalysis;
 101 import sun.jvm.hotspot.utilities.RobustOopDeterminator;
 102 import sun.jvm.hotspot.utilities.SystemDictionaryHelper;
 103 import sun.jvm.hotspot.utilities.soql.JSJavaFactory;
 104 import sun.jvm.hotspot.utilities.soql.JSJavaFactoryImpl;
 105 import sun.jvm.hotspot.utilities.soql.JSJavaScriptEngine;
 106 
 107 public class CommandProcessor {
 108 
 109     volatile boolean quit;
 110 
 111     public abstract static class DebuggerInterface {
 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             boolean allThreads = false;
 509             public void doit(Tokens t) {
 510                 if (t.countTokens() != 1) {
 511                     usage();
 512                     return;
 513                 }
 514                 String name = t.nextToken();
 515                 Address a = null;
 516                 try {
 517                     a = VM.getVM().getDebugger().parseAddress(name);
 518                 } catch (NumberFormatException e) { }
 519                 if (a != null) {
 520                     // only nmethod, Method, MethodData and InstanceKlass needed to
 521                     // dump replay data
 522 
 523                     CodeBlob cb = VM.getVM().getCodeCache().findBlob(a);
 524                     if (cb != null && (cb instanceof NMethod)) {
 525                         ((NMethod)cb).dumpReplayData(out);
 526                         return;
 527                     }
 528                     // assume it is Metadata
 529                     Metadata meta = Metadata.instantiateWrapperFor(a);
 530                     if (meta != null) {
 531                         meta.dumpReplayData(out);
 532                     } else {
 533                         usage();
 534                         return;
 535                     }
 536                 }
 537                 // Not an address
 538                 allThreads = name.equals("-a");
 539                 VM.getVM().getThreads().doJavaThreads((thread) -> {
 540                     ByteArrayOutputStream bos = new ByteArrayOutputStream();
 541                     thread.printThreadIDOn(new PrintStream(bos));
 542                     if (allThreads || 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() == VM.Flags_DEFAULT)) {
 653                                     // only print flags which aren't their defaults
 654                                     continue;
 655                                 }
 656                                 out.println(flag.getName() + " = " + flag.getValue() + " " + flag.getOriginString());
 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             boolean allThreads = false;
 895             public void doit(Tokens t) {
 896                 if (t.countTokens() != 1) {
 897                     usage();
 898                 } else {
 899                     String name = t.nextToken();
 900                     allThreads = name.equals("-a");
 901                     VM.getVM().getThreads().doJavaThreads((thread) -> {
 902                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
 903                         thread.printThreadIDOn(new PrintStream(bos));
 904                         if (allThreads || 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                     VM.getVM().getThreads().doJavaThreads((thread) -> {
 930                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
 931                         thread.printThreadIDOn(new PrintStream(bos));
 932                         if (all || bos.toString().equals(name)) {
 933                           if (thread instanceof CompilerThread) {
 934                             CompilerThread ct = (CompilerThread)thread;
 935                             out.println(ct);
 936                             ciEnv env = ct.env();
 937                             if (env != null) {
 938                               Compile c = env.compilerData();
 939                               c.cfg().dump(out);
 940                             }
 941                           }
 942                         }
 943                     });
 944                 }
 945             }
 946         },
 947         new Command("dumpilt", "dumpilt { -a | id }", false) {
 948             // dumps the InlineTree of a C2 compile
 949             public void doit(Tokens t) {
 950                 if (t.countTokens() != 1) {
 951                     usage();
 952                 } else {
 953                     String name = t.nextToken();
 954                     boolean all = name.equals("-a");
 955                     VM.getVM().getThreads().doJavaThreads((thread) -> {
 956                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
 957                         thread.printThreadIDOn(new PrintStream(bos));
 958                         if (all || bos.toString().equals(name)) {
 959                             if (thread instanceof CompilerThread) {
 960                                 CompilerThread ct = (CompilerThread)thread;
 961                                 ciEnv env = ct.env();
 962                                 if (env != null) {
 963                                     Compile c = env.compilerData();
 964                                     InlineTree ilt = c.ilt();
 965                                     if (ilt != null) {
 966                                         ilt.print(out);
 967                                     }
 968                                 }
 969                             }
 970                         }
 971                     });
 972                 }
 973             }
 974         },
 975         new Command("vmstructsdump", "vmstructsdump", false) {
 976             public void doit(Tokens t) {
 977                 if (t.countTokens() != 0) {
 978                     usage();
 979                     return;
 980                 }
 981 
 982                 // Dump a copy of the type database in a form that can
 983                 // be read back.
 984                 Iterator i = agent.getTypeDataBase().getTypes();
 985                 // Make sure the types are emitted in an order than can be read back in
 986                 HashSet emitted = new HashSet();
 987                 Stack pending = new Stack();
 988                 while (i.hasNext()) {
 989                     Type n = (Type)i.next();
 990                     if (emitted.contains(n.getName())) {
 991                         continue;
 992                     }
 993 
 994                     while (n != null && !emitted.contains(n.getName())) {
 995                         pending.push(n);
 996                         n = n.getSuperclass();
 997                     }
 998                     while (!pending.empty()) {
 999                         n = (Type)pending.pop();
1000                         dumpType(n);
1001                         emitted.add(n.getName());
1002                     }
1003                 }
1004                 i = agent.getTypeDataBase().getTypes();
1005                 while (i.hasNext()) {
1006                     dumpFields((Type)i.next(), false);
1007                 }
1008             }
1009         },
1010 
1011         new Command("inspect", "inspect expression", false) {
1012             public void doit(Tokens t) {
1013                 if (t.countTokens() != 1) {
1014                     usage();
1015                 } else {
1016                     Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
1017                     SimpleTreeNode node = null;
1018                     if (VM.getVM().getUniverse().heap().isInReserved(a)) {
1019                         OopHandle handle = a.addOffsetToAsOopHandle(0);
1020                         Oop oop = VM.getVM().getObjectHeap().newOop(handle);
1021                         node = new OopTreeNodeAdapter(oop, null);
1022 
1023                         out.println("instance of " + node.getValue() + " @ " + a +
1024                                     " (size = " + oop.getObjectSize() + ")");
1025                     } else if (VM.getVM().getCodeCache().contains(a)) {
1026                         CodeBlob blob = VM.getVM().getCodeCache().findBlobUnsafe(a);
1027                         a = blob.headerBegin();
1028                     }
1029                     if (node == null) {
1030                         Type type = VM.getVM().getTypeDataBase().guessTypeForAddress(a);
1031                         if (type == null && VM.getVM().isSharingEnabled()) {
1032                             // Check if the value falls in the _md_region
1033                             Address loc1 = a.getAddressAt(0);
1034                             FileMapInfo cdsFileMapInfo = VM.getVM().getFileMapInfo();
1035                             if (cdsFileMapInfo.inCopiedVtableSpace(loc1)) {
1036                                type = cdsFileMapInfo.getTypeForVptrAddress(loc1);
1037                             }
1038 
1039                         }
1040                         if (type != null) {
1041                             out.println("Type is " + type.getName() + " (size of " + type.getSize() + ")");
1042                             node = new CTypeTreeNodeAdapter(a, type, null);
1043                         }
1044                     }
1045                     if (node != null) {
1046                         printNode(node);
1047                     }
1048                 }
1049             }
1050         },
1051         new Command("jhisto", "jhisto", false) {
1052             public void doit(Tokens t) {
1053                  ObjectHistogram histo = new ObjectHistogram();
1054                  histo.run(out, err);
1055             }
1056         },
1057         new Command("jstack", "jstack [-v]", false) {
1058             public void doit(Tokens t) {
1059                 boolean verbose = false;
1060                 if (t.countTokens() > 0 && t.nextToken().equals("-v")) {
1061                     verbose = true;
1062                 }
1063                 StackTrace jstack = new StackTrace(verbose, true);
1064                 jstack.run(out);
1065             }
1066         },
1067         new Command("print", "print expression", false) {
1068             public void doit(Tokens t) {
1069                 if (t.countTokens() != 1) {
1070                     usage();
1071                 } else {
1072                     Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
1073                     HTMLGenerator gen = new HTMLGenerator(false);
1074                     out.println(gen.genHTML(a));
1075                 }
1076             }
1077         },
1078         new Command("printas", "printas type expression", false) {
1079             public void doit(Tokens t) {
1080                 if (t.countTokens() != 2) {
1081                     usage();
1082                 } else {
1083                     Type type = agent.getTypeDataBase().lookupType(t.nextToken());
1084                     Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
1085                     CTypeTreeNodeAdapter node = new CTypeTreeNodeAdapter(a, type, null);
1086 
1087                     out.println("pointer to " + type + " @ " + a +
1088                                 " (size = " + type.getSize() + ")");
1089                     printNode(node);
1090                 }
1091             }
1092         },
1093         new Command("printstatics", "printstatics [ type ]", false) {
1094             public void doit(Tokens t) {
1095                 if (t.countTokens() > 1) {
1096                     usage();
1097                 } else {
1098                     if (t.countTokens() == 0) {
1099                         out.println("All known static fields");
1100                         printNode(new CTypeTreeNodeAdapter(agent.getTypeDataBase().getTypes()));
1101                     } else {
1102                         Type type = agent.getTypeDataBase().lookupType(t.nextToken());
1103                         out.println("Static fields of " + type.getName());
1104                         printNode(new CTypeTreeNodeAdapter(type));
1105                     }
1106                 }
1107             }
1108         },
1109         new Command("pmap", "pmap", false) {
1110             public void doit(Tokens t) {
1111                 PMap pmap = new PMap();
1112                 pmap.run(out, debugger.getAgent().getDebugger());
1113             }
1114         },
1115         new Command("pstack", "pstack [-v]", false) {
1116             public void doit(Tokens t) {
1117                 boolean verbose = false;
1118                 if (t.countTokens() > 0 && t.nextToken().equals("-v")) {
1119                     verbose = true;
1120                 }
1121                 PStack pstack = new PStack(verbose, true);
1122                 pstack.run(out, debugger.getAgent().getDebugger());
1123             }
1124         },
1125         new Command("quit", true) {
1126             public void doit(Tokens t) {
1127                 if (t.countTokens() != 0) {
1128                     usage();
1129                 } else {
1130                     debugger.detach();
1131                     quit = true;
1132                 }
1133             }
1134         },
1135         new Command("echo", "echo [ true | false ]", true) {
1136             public void doit(Tokens t) {
1137                 if (t.countTokens() == 0) {
1138                     out.println("echo is " + doEcho);
1139                 } else if (t.countTokens() == 1) {
1140                     doEcho = Boolean.valueOf(t.nextToken()).booleanValue();
1141                 } else {
1142                     usage();
1143                 }
1144             }
1145         },
1146         new Command("versioncheck", "versioncheck [ true | false ]", true) {
1147             public void doit(Tokens t) {
1148                 if (t.countTokens() == 0) {
1149                     out.println("versioncheck is " +
1150                                 (System.getProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck") == null));
1151                 } else if (t.countTokens() == 1) {
1152                     if (Boolean.valueOf(t.nextToken()).booleanValue()) {
1153                         System.setProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck", null);
1154                     } else {
1155                         System.setProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck", "true");
1156                     }
1157                 } else {
1158                     usage();
1159                 }
1160             }
1161         },
1162         new Command("scanoops", "scanoops start end [ type ]", false) {
1163             public void doit(Tokens t) {
1164                 if (t.countTokens() != 2 && t.countTokens() != 3) {
1165                     usage();
1166                 } else {
1167                     long stride = VM.getVM().getAddressSize();
1168                     Address base = VM.getVM().getDebugger().parseAddress(t.nextToken());
1169                     Address end  = VM.getVM().getDebugger().parseAddress(t.nextToken());
1170                     Klass klass = null;
1171                     if (t.countTokens() == 1) {
1172                         klass = SystemDictionaryHelper.findInstanceKlass(t.nextToken());
1173                         if (klass == null) {
1174                             out.println("No such type.");
1175                             return;
1176                         }
1177                     }
1178                     while (base != null && base.lessThan(end)) {
1179                         long step = stride;
1180                         OopHandle handle = base.addOffsetToAsOopHandle(0);
1181                         if (RobustOopDeterminator.oopLooksValid(handle)) {
1182                             try {
1183                                 Oop oop = VM.getVM().getObjectHeap().newOop(handle);
1184                                 if (klass == null || oop.getKlass().isSubtypeOf(klass))
1185                                     out.println(handle.toString() + " " + oop.getKlass().getName().asString());
1186                                 step = oop.getObjectSize();
1187                             } catch (UnknownOopException ex) {
1188                                 // ok
1189                             } catch (RuntimeException ex) {
1190                                 ex.printStackTrace();
1191                             }
1192                         }
1193                         base = base.addOffsetTo(step);
1194                     }
1195                 }
1196             }
1197         },
1198         new Command("intConstant", "intConstant [ name [ value ] ]", true) {
1199             public void doit(Tokens t) {
1200                 if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 2) {
1201                     usage();
1202                     return;
1203                 }
1204                 HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
1205                 if (t.countTokens() == 1) {
1206                     String name = t.nextToken();
1207                     out.println("intConstant " + name + " " + db.lookupIntConstant(name));
1208                 } else if (t.countTokens() == 0) {
1209                     Iterator i = db.getIntConstants();
1210                     while (i.hasNext()) {
1211                         String name = (String)i.next();
1212                         out.println("intConstant " + name + " " + db.lookupIntConstant(name));
1213                     }
1214                 } else if (t.countTokens() == 2) {
1215                     String name = t.nextToken();
1216                     Integer value = Integer.valueOf(t.nextToken());
1217                     db.addIntConstant(name, value);
1218                 }
1219             }
1220         },
1221         new Command("longConstant", "longConstant [ name [ value ] ]", true) {
1222             public void doit(Tokens t) {
1223                 if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 2) {
1224                     usage();
1225                     return;
1226                 }
1227                 HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
1228                 if (t.countTokens() == 1) {
1229                     String name = t.nextToken();
1230                     out.println("longConstant " + name + " " + db.lookupLongConstant(name));
1231                 } else if (t.countTokens() == 0) {
1232                     Iterator i = db.getLongConstants();
1233                     while (i.hasNext()) {
1234                         String name = (String)i.next();
1235                         out.println("longConstant " + name + " " + db.lookupLongConstant(name));
1236                     }
1237                 } else if (t.countTokens() == 2) {
1238                     String name = t.nextToken();
1239                     Long value = Long.valueOf(t.nextToken());
1240                     db.addLongConstant(name, value);
1241                 }
1242             }
1243         },
1244         new Command("field", "field [ type [ name fieldtype isStatic offset address ] ]", true) {
1245             public void doit(Tokens t) {
1246                 if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 6) {
1247                     usage();
1248                     return;
1249                 }
1250                 if (t.countTokens() == 1) {
1251                     Type type = agent.getTypeDataBase().lookupType(t.nextToken());
1252                     dumpFields(type);
1253                 } else if (t.countTokens() == 0) {
1254                     Iterator i = agent.getTypeDataBase().getTypes();
1255                     while (i.hasNext()) {
1256                         dumpFields((Type)i.next());
1257                     }
1258                 } else {
1259                     BasicType containingType = (BasicType)agent.getTypeDataBase().lookupType(t.nextToken());
1260 
1261                     String fieldName = t.nextToken();
1262 
1263                     // The field's Type must already be in the database -- no exceptions
1264                     Type fieldType = agent.getTypeDataBase().lookupType(t.nextToken());
1265 
1266                     boolean isStatic = Boolean.valueOf(t.nextToken()).booleanValue();
1267                     long offset = Long.parseLong(t.nextToken());
1268                     Address staticAddress = parseAddress(t.nextToken());
1269                     if (isStatic && staticAddress == null) {
1270                         staticAddress = lookup(containingType.getName() + "::" + fieldName);
1271                     }
1272 
1273                     // check to see if the field already exists
1274                     Iterator i = containingType.getFields();
1275                     while (i.hasNext()) {
1276                         Field f = (Field) i.next();
1277                         if (f.getName().equals(fieldName)) {
1278                             if (f.isStatic() != isStatic) {
1279                                 throw new RuntimeException("static/nonstatic mismatch: " + t.input);
1280                             }
1281                             if (!isStatic) {
1282                                 if (f.getOffset() != offset) {
1283                                     throw new RuntimeException("bad redefinition of field offset: " + t.input);
1284                                 }
1285                             } else {
1286                                 if (!f.getStaticFieldAddress().equals(staticAddress)) {
1287                                     throw new RuntimeException("bad redefinition of field location: " + t.input);
1288                                 }
1289                             }
1290                             if (f.getType() != fieldType) {
1291                                 throw new RuntimeException("bad redefinition of field type: " + t.input);
1292                             }
1293                             return;
1294                         }
1295                     }
1296 
1297                     // Create field by type
1298                     HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
1299                     db.createField(containingType,
1300                                    fieldName, fieldType,
1301                                    isStatic,
1302                                    offset,
1303                                    staticAddress);
1304 
1305                 }
1306             }
1307 
1308         },
1309         new Command("tokenize", "tokenize ...", true) {
1310             public void doit(Tokens t) {
1311                 while (t.hasMoreTokens()) {
1312                     out.println("\"" + t.nextToken() + "\"");
1313                 }
1314             }
1315         },
1316         new Command("type", "type [ type [ name super isOop isInteger isUnsigned size ] ]", true) {
1317             public void doit(Tokens t) {
1318                 if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 6) {
1319                     usage();
1320                     return;
1321                 }
1322                 if (t.countTokens() == 6) {
1323                     String typeName = t.nextToken();
1324                     String superclassName = t.nextToken();
1325                     if (superclassName.equals("null")) {
1326                         superclassName = null;
1327                     }
1328                     boolean isOop = Boolean.valueOf(t.nextToken()).booleanValue();
1329                     boolean isInteger = Boolean.valueOf(t.nextToken()).booleanValue();
1330                     boolean isUnsigned = Boolean.valueOf(t.nextToken()).booleanValue();
1331                     long size = Long.parseLong(t.nextToken());
1332 
1333                     BasicType type = null;
1334                     try {
1335                         type = (BasicType)agent.getTypeDataBase().lookupType(typeName);
1336                     } catch (RuntimeException e) {
1337                     }
1338                     if (type != null) {
1339                         if (type.isOopType() != isOop) {
1340                             throw new RuntimeException("oop mismatch in type definition: " + t.input);
1341                         }
1342                         if (type.isCIntegerType() != isInteger) {
1343                             throw new RuntimeException("integer type mismatch in type definition: " + t.input);
1344                         }
1345                         if (type.isCIntegerType() && (((CIntegerType)type).isUnsigned()) != isUnsigned) {
1346                             throw new RuntimeException("unsigned mismatch in type definition: " + t.input);
1347                         }
1348                         if (type.getSuperclass() == null) {
1349                             if (superclassName != null) {
1350                                 if (type.getSize() == -1) {
1351                                     type.setSuperclass(agent.getTypeDataBase().lookupType(superclassName));
1352                                 } else {
1353                                     throw new RuntimeException("unexpected superclass in type definition: " + t.input);
1354                                 }
1355                             }
1356                         } else {
1357                             if (superclassName == null) {
1358                                 throw new RuntimeException("missing superclass in type definition: " + t.input);
1359                             }
1360                             if (!type.getSuperclass().getName().equals(superclassName)) {
1361                                 throw new RuntimeException("incorrect superclass in type definition: " + t.input);
1362                             }
1363                         }
1364                         if (type.getSize() != size) {
1365                             if (type.getSize() == -1) {
1366                                 type.setSize(size);
1367                             }
1368                             throw new RuntimeException("size mismatch in type definition: " + t.input);
1369                         }
1370                         return;
1371                     }
1372 
1373                     // Create type
1374                     HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
1375                     db.createType(typeName, superclassName, isOop, isInteger, isUnsigned, size);
1376                 } else if (t.countTokens() == 1) {
1377                     Type type = agent.getTypeDataBase().lookupType(t.nextToken());
1378                     dumpType(type);
1379                 } else {
1380                     Iterator i = agent.getTypeDataBase().getTypes();
1381                     // Make sure the types are emitted in an order than can be read back in
1382                     HashSet emitted = new HashSet();
1383                     Stack pending = new Stack();
1384                     while (i.hasNext()) {
1385                         Type n = (Type)i.next();
1386                         if (emitted.contains(n.getName())) {
1387                             continue;
1388                         }
1389 
1390                         while (n != null && !emitted.contains(n.getName())) {
1391                             pending.push(n);
1392                             n = n.getSuperclass();
1393                         }
1394                         while (!pending.empty()) {
1395                             n = (Type)pending.pop();
1396                             dumpType(n);
1397                             emitted.add(n.getName());
1398                         }
1399                     }
1400                 }
1401             }
1402 
1403         },
1404         new Command("source", "source filename", true) {
1405             public void doit(Tokens t) {
1406                 if (t.countTokens() != 1) {
1407                     usage();
1408                     return;
1409                 }
1410                 String file = t.nextToken();
1411                 BufferedReader savedInput = in;
1412                 try {
1413                     BufferedReader input = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
1414                     in = input;
1415                     run(false);
1416                 } catch (Exception e) {
1417                     out.println("Error: " + e);
1418                     if (verboseExceptions) {
1419                         e.printStackTrace(out);
1420                     }
1421                 } finally {
1422                     in = savedInput;
1423                 }
1424 
1425             }
1426         },
1427         new Command("search", "search [ heap | perm | rawheap | codecache | threads ] value", false) {
1428             public void doit(Tokens t) {
1429                 if (t.countTokens() != 2) {
1430                     usage();
1431                     return;
1432                 }
1433                 String type = t.nextToken();
1434                 final Address value = VM.getVM().getDebugger().parseAddress(t.nextToken());
1435                 final long stride = VM.getVM().getAddressSize();
1436                 if (type.equals("threads")) {
1437                     VM.getVM().getThreads().doJavaThreads((thread) -> {
1438                         Address base = thread.getStackBase();
1439                         Address end = thread.getLastJavaSP();
1440                         if (end == null) return;
1441                         if (end.lessThan(base)) {
1442                             Address tmp = base;
1443                             base = end;
1444                             end = tmp;
1445                         }
1446                         //out.println("Searching " + base + " " + end);
1447                         while (base != null && base.lessThan(end)) {
1448                             Address val = base.getAddressAt(0);
1449                             if (AddressOps.equal(val, value)) {
1450                                 ByteArrayOutputStream bos = new ByteArrayOutputStream();
1451                                 thread.printThreadIDOn(new PrintStream(bos));
1452                                 out.println("found on the stack of thread " + bos.toString() + " at " + base);
1453                             }
1454                             base = base.addOffsetTo(stride);
1455                         }
1456                     });
1457                 } else if (type.equals("rawheap")) {
1458                     RawHeapVisitor iterator = new RawHeapVisitor() {
1459                             public void prologue(long used) {
1460                             }
1461 
1462                             public void visitAddress(Address addr) {
1463                                 Address val = addr.getAddressAt(0);
1464                                 if (AddressOps.equal(val, value)) {
1465                                         out.println("found at " + addr);
1466                                 }
1467                             }
1468                             public void visitCompOopAddress(Address addr) {
1469                                 Address val = addr.getCompOopAddressAt(0);
1470                                 if (AddressOps.equal(val, value)) {
1471                                     out.println("found at " + addr);
1472                                 }
1473                             }
1474                             public void epilogue() {
1475                             }
1476                         };
1477                     VM.getVM().getObjectHeap().iterateRaw(iterator);
1478                 } else if (type.equals("heap")) {
1479                     HeapVisitor iterator = new DefaultHeapVisitor() {
1480                             public boolean doObj(Oop obj) {
1481                                 int index = 0;
1482                                 Address start = obj.getHandle();
1483                                 long end = obj.getObjectSize();
1484                                 while (index < end) {
1485                                     Address val = start.getAddressAt(index);
1486                                     if (AddressOps.equal(val, value)) {
1487                                         out.println("found in " + obj.getHandle());
1488                                         break;
1489                                     }
1490                                     index += 4;
1491                                 }
1492                                 return false;
1493                             }
1494                         };
1495                         VM.getVM().getObjectHeap().iterate(iterator);
1496                 } else if (type.equals("codecache")) {
1497                     CodeCacheVisitor v = new CodeCacheVisitor() {
1498                             public void prologue(Address start, Address end) {
1499                             }
1500                             public void visit(CodeBlob blob) {
1501                                 boolean printed = false;
1502                                 Address base = blob.getAddress();
1503                                 Address end = base.addOffsetTo(blob.getSize());
1504                                 while (base != null && base.lessThan(end)) {
1505                                     Address val = base.getAddressAt(0);
1506                                     if (AddressOps.equal(val, value)) {
1507                                         if (!printed) {
1508                                             printed = true;
1509                                             try {
1510                                                 blob.printOn(out);
1511                                             } catch (Exception e) {
1512                                                 out.println("Exception printing blob at " + base);
1513                                                 e.printStackTrace();
1514                                             }
1515                                         }
1516                                         out.println("found at " + base + "\n");
1517                                     }
1518                                     base = base.addOffsetTo(stride);
1519                                 }
1520                             }
1521                             public void epilogue() {
1522                             }
1523 
1524 
1525                         };
1526                     VM.getVM().getCodeCache().iterate(v);
1527 
1528                 }
1529             }
1530         },
1531         new Command("dumpcodecache", "dumpcodecache", false) {
1532             public void doit(Tokens t) {
1533                 if (t.countTokens() != 0) {
1534                     usage();
1535                 } else {
1536                     final PrintStream fout = out;
1537                     final HTMLGenerator gen = new HTMLGenerator(false);
1538                     CodeCacheVisitor v = new CodeCacheVisitor() {
1539                             public void prologue(Address start, Address end) {
1540                             }
1541                             public void visit(CodeBlob blob) {
1542                                 fout.println(gen.genHTML(blob.contentBegin()));
1543                             }
1544                             public void epilogue() {
1545                             }
1546 
1547 
1548                         };
1549                     VM.getVM().getCodeCache().iterate(v);
1550                 }
1551             }
1552         },
1553         new Command("where", "where { -a | id }", false) {
1554             boolean allThreads = false;
1555             public void doit(Tokens t) {
1556                 if (t.countTokens() != 1) {
1557                     usage();
1558                 } else {
1559                     String name = t.nextToken();
1560                     allThreads = name.equals("-a");
1561                     VM.getVM().getThreads().doJavaThreads((thread) -> {
1562                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
1563                         thread.printThreadIDOn(new PrintStream(bos));
1564                         if (allThreads || bos.toString().equals(name)) {
1565                             out.println("Thread " + bos.toString() + " Address: " + thread.getAddress());
1566                             HTMLGenerator gen = new HTMLGenerator(false);
1567                             try {
1568                                 out.println(gen.genHTMLForJavaStackTrace(thread));
1569                             } catch (Exception e) {
1570                                 err.println("Error: " + e);
1571                                 if (verboseExceptions) {
1572                                     e.printStackTrace(err);
1573                                 }
1574                             }
1575                             if (!allThreads) return;
1576                         }
1577                     });
1578                     if (!allThreads) out.println("Couldn't find thread " + name);
1579                 }
1580             }
1581         },
1582         new Command("thread", "thread { -a | id }", false) {
1583             boolean allThreads = false;
1584             public void doit(Tokens t) {
1585                 if (t.countTokens() != 1) {
1586                     usage();
1587                 } else {
1588                     String name = t.nextToken();
1589                     allThreads = name.equals("-a");
1590                     VM.getVM().getThreads().doJavaThreads((thread) -> {
1591                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
1592                         thread.printThreadIDOn(new PrintStream(bos));
1593                         if (allThreads || bos.toString().equals(name)) {
1594                             out.println("Thread " + bos.toString() + " Address " + thread.getAddress());
1595                             thread.printInfoOn(out);
1596                             out.println(" ");
1597                             if (!allThreads) return;
1598                         }
1599                     });
1600                     if (!allThreads) {
1601                         out.println("Couldn't find thread " + name);
1602                     }
1603                 }
1604             }
1605         },
1606 
1607         new Command("threads", false) {
1608             public void doit(Tokens t) {
1609                 if (t.countTokens() != 0) {
1610                     usage();
1611                 } else {
1612                     VM.getVM().getThreads().doJavaThreads((thread) -> {
1613                         thread.printThreadIDOn(out);
1614                         out.println(" " + thread.getThreadName());
1615                         thread.printInfoOn(out);
1616                         out.println("\n...");
1617                     });
1618                 }
1619             }
1620         },
1621 
1622         new Command("livenmethods", false) {
1623             public void doit(Tokens t) {
1624                 if (t.countTokens() != 0) {
1625                     usage();
1626                 } else {
1627                     ArrayList nmethods = new ArrayList();
1628                     HTMLGenerator gen = new HTMLGenerator(false);
1629                     VM.getVM().getThreads().doJavaThreads((thread) -> {
1630                         try {
1631                             for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) {
1632                                 if (vf instanceof CompiledVFrame) {
1633                                     NMethod c = ((CompiledVFrame)vf).getCode();
1634                                     if (!nmethods.contains(c)) {
1635                                         nmethods.add(c);
1636                                         out.println(gen.genHTML(c));
1637                                     }
1638                                 }
1639                             }
1640                         } catch (Exception e) {
1641                             e.printStackTrace();
1642                         }
1643                     });
1644                 }
1645             }
1646         },
1647         new Command("g1regiondetails", false) {
1648             public void doit(Tokens t) {
1649                 if (t.countTokens() != 0) {
1650                     usage();
1651                 } else {
1652                     CollectedHeap heap = VM.getVM().getUniverse().heap();
1653                     if (!(heap instanceof G1CollectedHeap)) {
1654                         out.println("This command is valid only for G1GC.");
1655                         return;
1656                     }
1657                     out.println("Region Details:");
1658                     ((G1CollectedHeap)heap).printRegionDetails(out);
1659                 }
1660             }
1661         },
1662         new Command("universe", false) {
1663             public void doit(Tokens t) {
1664                 if (t.countTokens() != 0) {
1665                     usage();
1666                 } else {
1667                     Universe u = VM.getVM().getUniverse();
1668                     out.println("Heap Parameters:");
1669                     u.heap().printOn(out);
1670                 }
1671             }
1672         },
1673         new Command("verbose", "verbose true | false", true) {
1674             public void doit(Tokens t) {
1675                 if (t.countTokens() != 1) {
1676                     usage();
1677                 } else {
1678                     verboseExceptions = Boolean.valueOf(t.nextToken()).booleanValue();
1679                 }
1680             }
1681         },
1682         new Command("assert", "assert true | false", true) {
1683             public void doit(Tokens t) {
1684                 if (t.countTokens() != 1) {
1685                     usage();
1686                 } else {
1687                     Assert.ASSERTS_ENABLED = Boolean.valueOf(t.nextToken()).booleanValue();
1688                 }
1689             }
1690         },
1691     };
1692 
1693     private boolean verboseExceptions = false;
1694     private ArrayList history = new ArrayList();
1695     private HashMap commands = new HashMap();
1696     private boolean doEcho = false;
1697 
1698     private Command findCommand(String key) {
1699         return (Command)commands.get(key);
1700     }
1701 
1702     public void printPrompt() {
1703         out.print("hsdb> ");
1704     }
1705 
1706     private DebuggerInterface debugger;
1707     private HotSpotAgent agent;
1708     private JSJavaScriptEngine jsengine;
1709     private BufferedReader in;
1710     private PrintStream out;
1711     private PrintStream err;
1712 
1713     // called before debuggee attach
1714     private void preAttach() {
1715         // nothing for now..
1716     }
1717 
1718     // called after debuggee attach
1719     private void postAttach() {
1720         // create JavaScript engine and start it
1721         try {
1722             jsengine = new JSJavaScriptEngine() {
1723                         private ObjectReader reader = new ObjectReader();
1724                         private JSJavaFactory factory = new JSJavaFactoryImpl();
1725                         public ObjectReader getObjectReader() {
1726                             return reader;
1727                         }
1728                         public JSJavaFactory getJSJavaFactory() {
1729                             return factory;
1730                         }
1731                         protected void quit() {
1732                             debugger.detach();
1733                             quit = true;
1734                         }
1735                         protected BufferedReader getInputReader() {
1736                             return in;
1737                         }
1738                         protected PrintStream getOutputStream() {
1739                             return out;
1740                         }
1741                         protected PrintStream getErrorStream() {
1742                             return err;
1743                         }
1744                    };
1745             try {
1746                 jsengine.defineFunction(this,
1747                      this.getClass().getMethod("registerCommand",
1748                                 new Class[] {
1749                                      String.class, String.class, String.class
1750                                 }));
1751             } catch (NoSuchMethodException exp) {
1752                   // should not happen, see below...!!
1753                   exp.printStackTrace();
1754             }
1755             jsengine.start();
1756         }
1757         catch (Exception ex) {
1758             System.out.println("Warning! JS Engine can't start, some commands will not be available.");
1759             if (verboseExceptions) {
1760                 ex.printStackTrace(out);
1761             }
1762         }
1763     }
1764 
1765     public void registerCommand(String cmd, String usage, final String func) {
1766         commands.put(cmd, new Command(cmd, usage, false) {
1767                               public void doit(Tokens t) {
1768                                   final int len = t.countTokens();
1769                                   Object[] args = new Object[len];
1770                                   for (int i = 0; i < len; i++) {
1771                                       args[i] = t.nextToken();
1772                                   }
1773                                   jsengine.call(func, args);
1774                               }
1775                           });
1776     }
1777 
1778     public void setOutput(PrintStream o) {
1779         out = o;
1780     }
1781 
1782     public void setErr(PrintStream e) {
1783         err = e;
1784     }
1785 
1786     public CommandProcessor(DebuggerInterface debugger, BufferedReader in, PrintStream out, PrintStream err) {
1787         this.debugger = debugger;
1788         this.agent = debugger.getAgent();
1789         this.in = in;
1790         this.out = out;
1791         this.err = err;
1792         for (int i = 0; i < commandList.length; i++) {
1793             Command c = commandList[i];
1794             if (commands.get(c.name) != null) {
1795                 throw new InternalError(c.name + " has multiple definitions");
1796             }
1797             commands.put(c.name, c);
1798         }
1799         if (debugger.isAttached()) {
1800             postAttach();
1801         }
1802     }
1803 
1804 
1805     public void run(boolean prompt) {
1806         // Process interactive commands.
1807         while (!quit) {
1808             if (prompt) printPrompt();
1809             String ln = null;
1810             try {
1811                 ln = in.readLine();
1812             } catch (IOException e) {
1813             }
1814             if (ln == null) {
1815                 if (prompt) err.println("Input stream closed.");
1816                 return;
1817             }
1818 
1819             executeCommand(ln, prompt);
1820         }
1821     }
1822 
1823     static Pattern historyPattern = Pattern.compile("((!\\*)|(!\\$)|(!!-?)|(!-?[0-9][0-9]*)|(![a-zA-Z][^ ]*))");
1824 
1825     public void executeCommand(String ln, boolean putInHistory) {
1826         if (ln.indexOf('!') != -1) {
1827             int size = history.size();
1828             if (size == 0) {
1829                 ln = "";
1830                 err.println("History is empty");
1831             } else {
1832                 StringBuffer result = new StringBuffer();
1833                 Matcher m = historyPattern.matcher(ln);
1834                 int start = 0;
1835                 while (m.find()) {
1836                     if (m.start() > start) {
1837                         result.append(ln.substring(start, m.start() - start));
1838                     }
1839                     start = m.end();
1840 
1841                     String cmd = m.group();
1842                     if (cmd.equals("!!")) {
1843                         result.append((String)history.get(history.size() - 1));
1844                     } else if (cmd.equals("!!-")) {
1845                         Tokens item = new Tokens((String)history.get(history.size() - 1));
1846                         item.trim(1);
1847                         result.append(item.join(" "));
1848                     } else if (cmd.equals("!*")) {
1849                         Tokens item = new Tokens((String)history.get(history.size() - 1));
1850                         item.nextToken();
1851                         result.append(item.join(" "));
1852                     } else if (cmd.equals("!$")) {
1853                         Tokens item = new Tokens((String)history.get(history.size() - 1));
1854                         result.append(item.at(item.countTokens() - 1));
1855                     } else {
1856                         String tail = cmd.substring(1);
1857                         switch (tail.charAt(0)) {
1858                         case '0':
1859                         case '1':
1860                         case '2':
1861                         case '3':
1862                         case '4':
1863                         case '5':
1864                         case '6':
1865                         case '7':
1866                         case '8':
1867                         case '9':
1868                         case '-': {
1869                             int index = Integer.parseInt(tail);
1870                             if (index < 0) {
1871                                 index = history.size() + index;
1872                             }
1873                             if (index > size) {
1874                                 err.println("No such history item");
1875                             } else {
1876                                 result.append((String)history.get(index));
1877                             }
1878                             break;
1879                         }
1880                         default: {
1881                             for (int i = history.size() - 1; i >= 0; i--) {
1882                                 String s = (String)history.get(i);
1883                                 if (s.startsWith(tail)) {
1884                                     result.append(s);
1885                                 }
1886                             }
1887                         }
1888                         }
1889                     }
1890                 }
1891                 if (result.length() == 0) {
1892                     err.println("malformed history reference");
1893                     ln = "";
1894                 } else {
1895                     if (start < ln.length()) {
1896                         result.append(ln.substring(start));
1897                     }
1898                     ln = result.toString();
1899                     if (!doEcho) {
1900                         out.println(ln);
1901                     }
1902                 }
1903             }
1904         }
1905 
1906         if (doEcho) {
1907             out.println("+ " + ln);
1908         }
1909 
1910         PrintStream redirect = null;
1911         Tokens t = new Tokens(ln);
1912         if (t.hasMoreTokens()) {
1913             boolean error = false;
1914             if (putInHistory) history.add(ln);
1915             int len = t.countTokens();
1916             if (len > 2) {
1917                 String r = t.at(len - 2);
1918                 if (r.equals(">") || r.equals(">>")) {
1919                     boolean append = r.length() == 2;
1920                     String file = t.at(len - 1);
1921                     try {
1922                         redirect = new PrintStream(new BufferedOutputStream(new FileOutputStream(file, append)));
1923                         t.trim(2);
1924                     } catch (Exception e) {
1925                         out.println("Error: " + e);
1926                         if (verboseExceptions) {
1927                             e.printStackTrace(out);
1928                         }
1929                         error = true;
1930                     }
1931                 }
1932             }
1933             if (!error) {
1934                 PrintStream savedout = out;
1935                 if (redirect != null) {
1936                     out = redirect;
1937                 }
1938                 try {
1939                     executeCommand(t);
1940                 } catch (Exception e) {
1941                     err.println("Error: " + e);
1942                     if (verboseExceptions) {
1943                         e.printStackTrace(err);
1944                     }
1945                 } finally {
1946                     if (redirect != null) {
1947                         out = savedout;
1948                         redirect.close();
1949                     }
1950                 }
1951             }
1952         }
1953     }
1954 
1955     void executeCommand(Tokens args) {
1956         String cmd = args.nextToken();
1957 
1958         Command doit = findCommand(cmd);
1959 
1960         /*
1961          * Check for an unknown command
1962          */
1963         if (doit == null) {
1964             out.println("Unrecognized command.  Try help...");
1965         } else if (!debugger.isAttached() && !doit.okIfDisconnected) {
1966             out.println("Command not valid until attached to a VM");
1967         } else {
1968             try {
1969                 doit.doit(args);
1970             } catch (Exception e) {
1971                 out.println("Error: " + e);
1972                 if (verboseExceptions) {
1973                     e.printStackTrace(out);
1974                 }
1975             }
1976         }
1977     }
1978 }