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