1 /*
   2  * Copyright 2005-2008 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  20  * CA 95054 USA or visit www.sun.com if you need additional information or
  21  * have any 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("flags", "flags [ flag ]", false) {
 432             public void doit(Tokens t) {
 433                 int tokens = t.countTokens();
 434                 if (tokens != 0 && tokens != 1) {
 435                     usage();
 436                 } else {
 437                     String name = tokens > 0 ? t.nextToken() : null;
 438 
 439                     VM.Flag[] flags = VM.getVM().getCommandLineFlags();
 440                     if (flags == null) {
 441                         out.println("Command Flag info not available (use 1.4.1_03 or later)!");
 442                     } else {
 443                         boolean printed = false;
 444                         for (int f = 0; f < flags.length; f++) {
 445                             VM.Flag flag = flags[f];
 446                             if (name == null || flag.getName().equals(name)) {
 447                                 out.println(flag.getName() + " = " + flag.getValue());
 448                                 printed = true;
 449                             }
 450                         }
 451                         if (name != null && !printed) {
 452                             out.println("Couldn't find flag: " + name);
 453                         }
 454                     }
 455                 }
 456             }
 457         },
 458         new Command("help", "help [ command ]", true) {
 459             public void doit(Tokens t) {
 460                 int tokens = t.countTokens();
 461                 Command cmd = null;
 462                 if (tokens == 1) {
 463                     cmd = findCommand(t.nextToken());
 464                 }
 465 
 466                 if (cmd != null) {
 467                     cmd.usage();
 468                 } else if (tokens == 0) {
 469                     out.println("Available commands:");
 470                     Object[] keys = commands.keySet().toArray();
 471                     Arrays.sort(keys, new Comparator() {
 472                              public int compare(Object o1, Object o2) {
 473                                  return o1.toString().compareTo(o2.toString());
 474                              }
 475                           });
 476                     for (int i = 0; i < keys.length; i++) {
 477                         out.print("  ");
 478                         out.println(((Command)commands.get(keys[i])).usage);
 479                     }
 480                 }
 481             }
 482         },
 483         new Command("history", "history", true) {
 484             public void doit(Tokens t) {
 485                 int tokens = t.countTokens();
 486                 if (tokens != 0 && (tokens != 1 || !t.nextToken().equals("-h"))) {
 487                     usage();
 488                     return;
 489                 }
 490                 boolean printIndex = tokens == 0;
 491                 for (int i = 0; i < history.size(); i++) {
 492                     if (printIndex) out.print(i + " ");
 493                     out.println(history.get(i));
 494                 }
 495             }
 496         },
 497         new Command("inspect", "inspect expression", false) {
 498             public void doit(Tokens t) {
 499                 if (t.countTokens() != 1) {
 500                     usage();
 501                 } else {
 502                     Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
 503                     SimpleTreeNode node = null;
 504                     if (VM.getVM().getUniverse().heap().isInReserved(a)) {
 505                         OopHandle handle = a.addOffsetToAsOopHandle(0);
 506                         Oop oop = VM.getVM().getObjectHeap().newOop(handle);
 507                         node = new OopTreeNodeAdapter(oop, null);
 508 
 509                         out.println("instance of " + node.getValue() + " @ " + a +
 510                                     " (size = " + oop.getObjectSize() + ")");
 511                     } else if (VM.getVM().getCodeCache().contains(a)) {
 512                         CodeBlob blob = VM.getVM().getCodeCache().findBlobUnsafe(a);
 513                         a = blob.headerBegin();
 514                     }
 515                     if (node == null) {
 516                         Type type = VM.getVM().getTypeDataBase().guessTypeForAddress(a);
 517                         if (type != null) {
 518                             out.println("Type is " + type.getName() + " (size of " + type.getSize() + ")");
 519                             node = new CTypeTreeNodeAdapter(a, type, null);
 520                         }
 521                     }
 522                     if (node != null) {
 523                         printNode(node);
 524                     }
 525                 }
 526             }
 527         },
 528         new Command("jhisto", "jhisto", false) {
 529             public void doit(Tokens t) {
 530                  ObjectHistogram histo = new ObjectHistogram();
 531                  histo.run(out, err);
 532             }
 533         },
 534         new Command("jstack", "jstack [-v]", false) {
 535             public void doit(Tokens t) {
 536                 boolean verbose = false;
 537                 if (t.countTokens() > 0 && t.nextToken().equals("-v")) {
 538                     verbose = true;
 539                 }
 540                 StackTrace jstack = new StackTrace(verbose, true);
 541                 jstack.run(out);
 542             }
 543         },
 544         new Command("print", "print expression", false) {
 545             public void doit(Tokens t) {
 546                 if (t.countTokens() != 1) {
 547                     usage();
 548                 } else {
 549                     Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
 550                     HTMLGenerator gen = new HTMLGenerator(false);
 551                     out.println(gen.genHTML(a));
 552                 }
 553             }
 554         },
 555         new Command("printas", "printas type expression", false) {
 556             public void doit(Tokens t) {
 557                 if (t.countTokens() != 2) {
 558                     usage();
 559                 } else {
 560                     Type type = agent.getTypeDataBase().lookupType(t.nextToken());
 561                     Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
 562                     CTypeTreeNodeAdapter node = new CTypeTreeNodeAdapter(a, type, null);
 563 
 564                     out.println("pointer to " + type + " @ " + a +
 565                                 " (size = " + type.getSize() + ")");
 566                     printNode(node);
 567                 }
 568             }
 569         },
 570         new Command("symbol", "symbol name", false) {
 571             public void doit(Tokens t) {
 572                 if (t.countTokens() != 1) {
 573                     usage();
 574                 } else {
 575                     String symbol = t.nextToken();
 576                     Address a = lookup(symbol);
 577                     out.println(symbol + " = " + a);
 578                 }
 579             }
 580         },
 581         new Command("printstatics", "printstatics [ type ]", false) {
 582             public void doit(Tokens t) {
 583                 if (t.countTokens() > 1) {
 584                     usage();
 585                 } else {
 586                     if (t.countTokens() == 0) {
 587                         out.println("All known static fields");
 588                         printNode(new CTypeTreeNodeAdapter(agent.getTypeDataBase().getTypes()));
 589                     } else {
 590                         Type type = agent.getTypeDataBase().lookupType(t.nextToken());
 591                         out.println("Static fields of " + type.getName());
 592                         printNode(new CTypeTreeNodeAdapter(type));
 593                     }
 594                 }
 595             }
 596         },
 597         new Command("pmap", "pmap", false) {
 598             public void doit(Tokens t) {
 599                 PMap pmap = new PMap();
 600                 pmap.run(out, debugger.getAgent().getDebugger());
 601             }
 602         },
 603         new Command("pstack", "pstack [-v]", false) {
 604             public void doit(Tokens t) {
 605                 boolean verbose = false;
 606                 if (t.countTokens() > 0 && t.nextToken().equals("-v")) {
 607                     verbose = true;
 608                 }
 609                 PStack pstack = new PStack(verbose, true);
 610                 pstack.run(out, debugger.getAgent().getDebugger());
 611             }
 612         },
 613         new Command("quit", true) {
 614             public void doit(Tokens t) {
 615                 if (t.countTokens() != 0) {
 616                     usage();
 617                 } else {
 618                     debugger.detach();
 619                     System.exit(0);
 620                 }
 621             }
 622         },
 623         new Command("echo", "echo [ true | false ]", true) {
 624             public void doit(Tokens t) {
 625                 if (t.countTokens() == 0) {
 626                     out.println("echo is " + doEcho);
 627                 } else if (t.countTokens() == 1) {
 628                     doEcho = Boolean.valueOf(t.nextToken()).booleanValue();
 629                 } else {
 630                     usage();
 631                 }
 632             }
 633         },
 634         new Command("versioncheck", "versioncheck [ true | false ]", true) {
 635             public void doit(Tokens t) {
 636                 if (t.countTokens() == 0) {
 637                     out.println("versioncheck is " +
 638                                 (System.getProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck") == null));
 639                 } else if (t.countTokens() == 1) {
 640                     if (Boolean.valueOf(t.nextToken()).booleanValue()) {
 641                         System.setProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck", null);
 642                     } else {
 643                         System.setProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck", "true");
 644                     }
 645                 } else {
 646                     usage();
 647                 }
 648             }
 649         },
 650         new Command("scanoops", "scanoops start end [ type ]", false) {
 651             public void doit(Tokens t) {
 652                 if (t.countTokens() != 2 && t.countTokens() != 3) {
 653                     usage();
 654                 } else {
 655                     long stride = VM.getVM().getAddressSize();
 656                     Address base = VM.getVM().getDebugger().parseAddress(t.nextToken());
 657                     Address end  = VM.getVM().getDebugger().parseAddress(t.nextToken());
 658                     Klass klass = null;
 659                     if (t.countTokens() == 1) {
 660                         klass = SystemDictionaryHelper.findInstanceKlass(t.nextToken());
 661                     }
 662                     while (base != null && base.lessThan(end)) {
 663                         long step = stride;
 664                         OopHandle handle = base.addOffsetToAsOopHandle(0);
 665                         if (RobustOopDeterminator.oopLooksValid(handle)) {
 666                             try {
 667                                 Oop oop = VM.getVM().getObjectHeap().newOop(handle);
 668                                 if (klass == null || oop.getKlass().isSubtypeOf(klass))
 669                                     out.println(handle.toString() + " " + oop.getKlass().getName().asString());
 670                                 step = oop.getObjectSize();
 671                             } catch (UnknownOopException ex) {
 672                                 // ok
 673                             } catch (RuntimeException ex) {
 674                                 ex.printStackTrace();
 675                             }
 676                         }
 677                         base = base.addOffsetTo(step);
 678                     }
 679                 }
 680             }
 681         },
 682         new Command("field", "field [ type [ name fieldtype isStatic offset address ] ]", true) {
 683             public void doit(Tokens t) {
 684                 if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 6) {
 685                     usage();
 686                     return;
 687                 }
 688                 if (t.countTokens() == 1) {
 689                     Type type = agent.getTypeDataBase().lookupType(t.nextToken());
 690                     dumpFields(type);
 691                 } else if (t.countTokens() == 0) {
 692                     Iterator i = agent.getTypeDataBase().getTypes();
 693                     while (i.hasNext()) {
 694                         dumpFields((Type)i.next());
 695                     }
 696                 } else {
 697                     BasicType containingType = (BasicType)agent.getTypeDataBase().lookupType(t.nextToken());
 698 
 699                     String fieldName = t.nextToken();
 700 
 701                     // The field's Type must already be in the database -- no exceptions
 702                     Type fieldType = agent.getTypeDataBase().lookupType(t.nextToken());
 703 
 704                     boolean isStatic = Boolean.valueOf(t.nextToken()).booleanValue();
 705                     long offset = Long.parseLong(t.nextToken());
 706                     Address staticAddress = parseAddress(t.nextToken());
 707                     if (isStatic && staticAddress == null) {
 708                         staticAddress = lookup(containingType.getName() + "::" + fieldName);
 709                     }
 710 
 711                     // check to see if the field already exists
 712                     Iterator i = containingType.getFields();
 713                     while (i.hasNext()) {
 714                         Field f = (Field) i.next();
 715                         if (f.getName().equals(fieldName)) {
 716                             if (f.isStatic() != isStatic) {
 717                                 throw new RuntimeException("static/nonstatic mismatch: " + t.input);
 718                             }
 719                             if (!isStatic) {
 720                                 if (f.getOffset() != offset) {
 721                                     throw new RuntimeException("bad redefinition of field offset: " + t.input);
 722                                 }
 723                             } else {
 724                                 if (!f.getStaticFieldAddress().equals(staticAddress)) {
 725                                     throw new RuntimeException("bad redefinition of field location: " + t.input);
 726                                 }
 727                             }
 728                             if (f.getType() != fieldType) {
 729                                 throw new RuntimeException("bad redefinition of field type: " + t.input);
 730                             }
 731                             return;
 732                         }
 733                     }
 734 
 735                     // Create field by type
 736                     HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
 737                     db.createField(containingType,
 738                                    fieldName, fieldType,
 739                                    isStatic,
 740                                    offset,
 741                                    staticAddress);
 742 
 743                 }
 744             }
 745 
 746         },
 747         new Command("tokenize", "tokenize ...", true) {
 748             public void doit(Tokens t) {
 749                 while (t.hasMoreTokens()) {
 750                     out.println("\"" + t.nextToken() + "\"");
 751                 }
 752             }
 753         },
 754         new Command("type", "type [ type [ name super isOop isInteger isUnsigned size ] ]", true) {
 755             public void doit(Tokens t) {
 756                 if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 6) {
 757                     usage();
 758                     return;
 759                 }
 760                 if (t.countTokens() == 6) {
 761                     String typeName = t.nextToken();
 762                     String superclassName = t.nextToken();
 763                     if (superclassName.equals("null")) {
 764                         superclassName = null;
 765                     }
 766                     boolean isOop = Boolean.valueOf(t.nextToken()).booleanValue();
 767                     boolean isInteger = Boolean.valueOf(t.nextToken()).booleanValue();
 768                     boolean isUnsigned = Boolean.valueOf(t.nextToken()).booleanValue();
 769                     long size = Long.parseLong(t.nextToken());
 770 
 771                     BasicType type = null;
 772                     try {
 773                         type = (BasicType)agent.getTypeDataBase().lookupType(typeName);
 774                     } catch (RuntimeException e) {
 775                     }
 776                     if (type != null) {
 777                         if (type.isOopType() != isOop) {
 778                             throw new RuntimeException("oop mismatch in type definition: " + t.input);
 779                         }
 780                         if (type.isCIntegerType() != isInteger) {
 781                             throw new RuntimeException("integer type mismatch in type definition: " + t.input);
 782                         }
 783                         if (type.isCIntegerType() && (((CIntegerType)type).isUnsigned()) != isUnsigned) {
 784                             throw new RuntimeException("unsigned mismatch in type definition: " + t.input);
 785                         }
 786                         if (type.getSuperclass() == null) {
 787                             if (superclassName != null) {
 788                                 if (type.getSize() == -1) {
 789                                     type.setSuperclass(agent.getTypeDataBase().lookupType(superclassName));
 790                                 } else {
 791                                     throw new RuntimeException("unexpected superclass in type definition: " + t.input);
 792                                 }
 793                             }
 794                         } else {
 795                             if (superclassName == null) {
 796                                 throw new RuntimeException("missing superclass in type definition: " + t.input);
 797                             }
 798                             if (!type.getSuperclass().getName().equals(superclassName)) {
 799                                 throw new RuntimeException("incorrect superclass in type definition: " + t.input);
 800                             }
 801                         }
 802                         if (type.getSize() != size) {
 803                             if (type.getSize() == -1) {
 804                                 type.setSize(size);
 805                             }
 806                             throw new RuntimeException("size mismatch in type definition: " + t.input);
 807                         }
 808                         return;
 809                     }
 810 
 811                     // Create type
 812                     HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
 813                     db.createType(typeName, superclassName, isOop, isInteger, isUnsigned, size);
 814                 } else if (t.countTokens() == 1) {
 815                     Type type = agent.getTypeDataBase().lookupType(t.nextToken());
 816                     dumpType(type);
 817                 } else {
 818                     Iterator i = agent.getTypeDataBase().getTypes();
 819                     while (i.hasNext()) {
 820                         dumpType((Type)i.next());
 821                     }
 822                 }
 823             }
 824 
 825         },
 826         new Command("source", "source filename", true) {
 827             public void doit(Tokens t) {
 828                 if (t.countTokens() != 1) {
 829                     usage();
 830                     return;
 831                 }
 832                 String file = t.nextToken();
 833                 BufferedReader savedInput = in;
 834                 try {
 835                     BufferedReader input = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
 836                     in = input;
 837                     run(false);
 838                 } catch (Exception e) {
 839                     out.println("Error: " + e);
 840                     if (verboseExceptions) {
 841                         e.printStackTrace(out);
 842                     }
 843                 } finally {
 844                     in = savedInput;
 845                 }
 846 
 847             }
 848         },
 849         new Command("search", "search [ heap | codecache | threads ] value", false) {
 850             public void doit(Tokens t) {
 851                 if (t.countTokens() != 2) {
 852                     usage();
 853                 } else {
 854                     String type = t.nextToken();
 855                     final Address value = VM.getVM().getDebugger().parseAddress(t.nextToken());
 856                     final long stride = VM.getVM().getAddressSize();
 857                     if (type.equals("threads")) {
 858                         Threads threads = VM.getVM().getThreads();
 859                         for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
 860                             Address base = thread.getBaseOfStackPointer();
 861                             Address end = thread.getLastJavaSP();
 862                             if (end == null) continue;
 863                             if (end.lessThan(base)) {
 864                                 Address tmp = base;
 865                                 base = end;
 866                                 end = tmp;
 867                             }
 868                             out.println("Searching " + base + " " + end);
 869                             while (base != null && base.lessThan(end)) {
 870                                 Address val = base.getAddressAt(0);
 871                                 if (AddressOps.equal(val, value)) {
 872                                     out.println(base);
 873                                 }
 874                                 base = base.addOffsetTo(stride);
 875                             }
 876                         }
 877                     } else if (type.equals("heap")) {
 878                         RawHeapVisitor iterator = new RawHeapVisitor() {
 879                                 public void prologue(long used) {
 880                                 }
 881 
 882                                 public void visitAddress(Address addr) {
 883                                     Address val = addr.getAddressAt(0);
 884                                     if (AddressOps.equal(val, value)) {
 885                                         out.println("found at " + addr);
 886                                     }
 887                                 }
 888                                 public void visitCompOopAddress(Address addr) {
 889                                     Address val = addr.getCompOopAddressAt(0);
 890                                     if (AddressOps.equal(val, value)) {
 891                                         out.println("found at " + addr);
 892                                     }
 893                                 }
 894                                 public void epilogue() {
 895                                 }
 896                             };
 897                         VM.getVM().getObjectHeap().iterateRaw(iterator);
 898                     } else if (type.equals("codecache")) {
 899                         CodeCacheVisitor v = new CodeCacheVisitor() {
 900                                 public void prologue(Address start, Address end) {
 901                                 }
 902                                 public void visit(CodeBlob blob) {
 903                                     boolean printed = false;
 904                                     Address base = blob.getAddress();
 905                                     Address end = base.addOffsetTo(blob.getSize());
 906                                     while (base != null && base.lessThan(end)) {
 907                                         Address val = base.getAddressAt(0);
 908                                         if (AddressOps.equal(val, value)) {
 909                                             if (!printed) {
 910                                                 printed = true;
 911                                                 blob.printOn(out);
 912                                             }
 913                                             out.println("found at " + base + "\n");
 914                                         }
 915                                         base = base.addOffsetTo(stride);
 916                                     }
 917                                 }
 918                                 public void epilogue() {
 919                                 }
 920 
 921 
 922                             };
 923                         VM.getVM().getCodeCache().iterate(v);
 924 
 925                     }
 926                 }
 927             }
 928         },
 929         new Command("dumpcodecache", "dumpcodecache", false) {
 930             public void doit(Tokens t) {
 931                 if (t.countTokens() != 0) {
 932                     usage();
 933                 } else {
 934                     final PrintStream fout = out;
 935                     final HTMLGenerator gen = new HTMLGenerator(false);
 936                     CodeCacheVisitor v = new CodeCacheVisitor() {
 937                             public void prologue(Address start, Address end) {
 938                             }
 939                             public void visit(CodeBlob blob) {
 940                                 fout.println(gen.genHTML(blob.instructionsBegin()));
 941                             }
 942                             public void epilogue() {
 943                             }
 944                             
 945                             
 946                         };
 947                     VM.getVM().getCodeCache().iterate(v);
 948                 }
 949             }
 950         },
 951         new Command("where", "where { -a | id }", false) {
 952             public void doit(Tokens t) {
 953                 if (t.countTokens() != 1) {
 954                     usage();
 955                 } else {
 956                     String name = t.nextToken();
 957                     Threads threads = VM.getVM().getThreads();
 958                     boolean all = name.equals("-a");
 959                     for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
 960                         StringWriter sw = new StringWriter();
 961                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
 962                         thread.printThreadIDOn(new PrintStream(bos));
 963                         if (all || bos.toString().equals(name)) {
 964                             HTMLGenerator gen = new HTMLGenerator(false);
 965                             out.println(gen.genHTMLForJavaStackTrace(thread));
 966                             if (!all) return;
 967                         }
 968                     }
 969                     if (!all) out.println("Couldn't find thread " + name);
 970                 }
 971             }
 972         },
 973 
 974         new Command("threads", false) {
 975             public void doit(Tokens t) {
 976                 if (t.countTokens() != 0) {
 977                     usage();
 978                 } else {
 979                     Threads threads = VM.getVM().getThreads();
 980                     for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
 981                         thread.printThreadIDOn(out);
 982                         out.println(" " + thread.getThreadName());
 983                     }
 984                 }
 985             }
 986         },
 987 
 988         new Command("livenmethods", false) {
 989             public void doit(Tokens t) {
 990                 if (t.countTokens() != 0) {
 991                     usage();
 992                 } else {
 993                     ArrayList nmethods = new ArrayList();
 994                     Threads threads = VM.getVM().getThreads();
 995                     HTMLGenerator gen = new HTMLGenerator(false);
 996                     for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
 997                         try {
 998                             for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) {
 999                                 if (vf instanceof CompiledVFrame) {
1000                                     NMethod c = ((CompiledVFrame)vf).getCode();
1001                                     if (!nmethods.contains(c)) {
1002                                         nmethods.add(c);
1003                                         out.println(gen.genHTML(c));
1004                                     }
1005                                 }
1006                             }
1007                         } catch (Exception e) {
1008                             e.printStackTrace();
1009                         }
1010                     }
1011                 }
1012             }
1013         },
1014         new Command("universe", false) {
1015             public void doit(Tokens t) {
1016                 if (t.countTokens() != 0) {
1017                     usage();
1018                 } else {
1019                     Universe u = VM.getVM().getUniverse();
1020                     out.println("Heap Parameters:");
1021                     u.heap().printOn(out);
1022                 }
1023             }
1024         },
1025         new Command("verbose", "verbose true | false", true) {
1026             public void doit(Tokens t) {
1027                 if (t.countTokens() != 1) {
1028                     usage();
1029                 } else {
1030                     verboseExceptions = Boolean.valueOf(t.nextToken()).booleanValue();
1031                 }
1032             }
1033         },
1034         new Command("assert", "assert true | false", true) {
1035             public void doit(Tokens t) {
1036                 if (t.countTokens() != 1) {
1037                     usage();
1038                 } else {
1039                     Assert.ASSERTS_ENABLED = Boolean.valueOf(t.nextToken()).booleanValue();
1040                 }
1041             }
1042         },
1043     };
1044 
1045     private boolean verboseExceptions = false;
1046     private ArrayList history = new ArrayList();
1047     private HashMap commands = new HashMap();
1048     private boolean doEcho = false;
1049 
1050     private Command findCommand(String key) {
1051         return (Command)commands.get(key);
1052     }
1053 
1054     public void printPrompt() {
1055         out.print("hsdb> ");
1056     }
1057 
1058     private DebuggerInterface debugger;
1059     private HotSpotAgent agent;
1060     private JSJavaScriptEngine jsengine;
1061     private BufferedReader in;
1062     private PrintStream out;
1063     private PrintStream err;
1064 
1065     // called before debuggee attach
1066     private void preAttach() {
1067         // nothing for now..
1068     }
1069 
1070     // called after debuggee attach
1071     private void postAttach() {
1072         // create JavaScript engine and start it
1073         jsengine = new JSJavaScriptEngine() {
1074                         private ObjectReader reader = new ObjectReader();
1075                         private JSJavaFactory factory = new JSJavaFactoryImpl();
1076                         public ObjectReader getObjectReader() {
1077                             return reader;
1078                         }
1079                         public JSJavaFactory getJSJavaFactory() {
1080                             return factory;
1081                         }
1082                         protected void quit() {
1083                             debugger.detach();
1084                             System.exit(0);
1085                         }
1086                         protected BufferedReader getInputReader() {
1087                             return in;
1088                         }
1089                         protected PrintStream getOutputStream() {
1090                             return out;
1091                         }
1092                         protected PrintStream getErrorStream() {
1093                             return err;
1094                         }
1095                    };
1096         try {
1097             jsengine.defineFunction(this,
1098                      this.getClass().getMethod("registerCommand",
1099                                 new Class[] {
1100                                      String.class, String.class, String.class
1101                                 }));
1102         } catch (NoSuchMethodException exp) {
1103             // should not happen, see below...!!
1104             exp.printStackTrace();
1105         }
1106         jsengine.start();
1107     }
1108 
1109     public void registerCommand(String cmd, String usage, final String func) {
1110         commands.put(cmd, new Command(cmd, usage, false) {
1111                               public void doit(Tokens t) {
1112                                   final int len = t.countTokens();
1113                                   Object[] args = new Object[len];
1114                                   for (int i = 0; i < len; i++) {
1115                                       args[i] = t.nextToken();
1116                                   }
1117                                   jsengine.call(func, args);
1118                               }
1119                           });
1120     }
1121 
1122     public void setOutput(PrintStream o) {
1123         out = o;
1124     }
1125 
1126     public void setErr(PrintStream e) {
1127         err = e;
1128     }
1129 
1130     public CommandProcessor(DebuggerInterface debugger, BufferedReader in, PrintStream out, PrintStream err) {
1131         this.debugger = debugger;
1132         this.agent = debugger.getAgent();
1133         this.in = in;
1134         this.out = out;
1135         this.err = err;
1136         for (int i = 0; i < commandList.length; i++) {
1137             Command c = commandList[i];
1138             commands.put(c.name, c);
1139         }
1140         if (debugger.isAttached()) {
1141             postAttach();
1142         }
1143     }
1144 
1145 
1146     public void run(boolean prompt) {
1147         // Process interactive commands.
1148         while (true) {
1149             if (prompt) printPrompt();
1150             String ln = null;
1151             try {
1152                 ln = in.readLine();
1153             } catch (IOException e) {
1154             }
1155             if (ln == null) {
1156                 if (prompt) err.println("Input stream closed.");
1157                 return;
1158             }
1159 
1160             executeCommand(ln);
1161         }
1162     }
1163 
1164     static Pattern historyPattern = Pattern.compile("((!\\*)|(!\\$)|(!!-?)|(!-?[0-9][0-9]*))");
1165 
1166     public void executeCommand(String ln) {
1167         if (ln.indexOf('!') != -1) {
1168             int size = history.size();
1169             if (size == 0) {
1170                 ln = "";
1171                 err.println("History is empty");
1172             } else {
1173                 StringBuffer result = new StringBuffer();
1174                 Matcher m = historyPattern.matcher(ln);
1175                 int start = 0;
1176                 while (m.find()) {
1177                     if (m.start() > start) {
1178                         result.append(ln.substring(start, m.start() - start));
1179                     }
1180                     start = m.end();
1181 
1182                     String cmd = m.group();
1183                     if (cmd.equals("!!")) {
1184                         result.append((String)history.get(history.size() - 1));
1185                     } else if (cmd.equals("!!-")) {
1186                         Tokens item = new Tokens((String)history.get(history.size() - 1));
1187                         item.trim(1);
1188                         result.append(item.join(" "));
1189                     } else if (cmd.equals("!*")) {
1190                         Tokens item = new Tokens((String)history.get(history.size() - 1));
1191                         item.nextToken();
1192                         result.append(item.join(" "));
1193                     } else if (cmd.equals("!$")) {
1194                         Tokens item = new Tokens((String)history.get(history.size() - 1));
1195                         result.append(item.at(item.countTokens() - 1));
1196                     } else {
1197                         String tail = cmd.substring(1);
1198                         int index = Integer.parseInt(tail);
1199                         if (index < 0) {
1200                             index = history.size() + index;
1201                         }
1202                         if (index > size) {
1203                             err.println("No such history item");
1204                         } else {
1205                             result.append((String)history.get(index));
1206                         }
1207                     }
1208                 }
1209                 if (result.length() == 0) {
1210                     err.println("malformed history reference");
1211                     ln = "";
1212                 } else {
1213                     if (start < ln.length()) {
1214                         result.append(ln.substring(start));
1215                     }
1216                     ln = result.toString();
1217                     if (!doEcho) {
1218                         out.println(ln);
1219                     }
1220                 }
1221             }
1222         }
1223 
1224         if (doEcho) {
1225             out.println("+ " + ln);
1226         }
1227 
1228         PrintStream redirect = null;
1229         Tokens t = new Tokens(ln);
1230         if (t.hasMoreTokens()) {
1231             boolean error = false;
1232             history.add(ln);
1233             int len = t.countTokens();
1234             if (len > 2) {
1235                 String r = t.at(len - 2);
1236                 if (r.equals(">") || r.equals(">>")) {
1237                     boolean append = r.length() == 2;
1238                     String file = t.at(len - 1);
1239                     try {
1240                         redirect = new PrintStream(new BufferedOutputStream(new FileOutputStream(file, append)));
1241                         t.trim(2);
1242                     } catch (Exception e) {
1243                         out.println("Error: " + e);
1244                         if (verboseExceptions) {
1245                             e.printStackTrace(out);
1246                         }
1247                         error = true;
1248                     }
1249                 }
1250             }
1251             if (!error) {
1252                 PrintStream savedout = out;
1253                 if (redirect != null) {
1254                     out = redirect;
1255                 }
1256                 try {
1257                     executeCommand(t);
1258                 } catch (Exception e) {
1259                     err.println("Error: " + e);
1260                     if (verboseExceptions) {
1261                         e.printStackTrace(err);
1262                     }
1263                 } finally {
1264                     if (redirect != null) {
1265                         out = savedout;
1266                         redirect.close();
1267                     }
1268                 }
1269             }
1270         }
1271     }
1272 
1273     void executeCommand(Tokens args) {
1274         String cmd = args.nextToken();
1275 
1276         Command doit = findCommand(cmd);
1277 
1278         /*
1279          * Check for an unknown command
1280          */
1281         if (doit == null) {
1282             out.println("Unrecognized command.  Try help...");
1283         } else if (!debugger.isAttached() && !doit.okIfDisconnected) {
1284             out.println("Command not valid until the attached to a VM");
1285         } else {
1286             try {
1287                 doit.doit(args);
1288             } catch (Exception e) {
1289                 out.println("Error: " + e);
1290                 if (verboseExceptions) {
1291                     e.printStackTrace(out);
1292                 }
1293             }
1294         }
1295     }
1296 }