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