1 /*
   2  * Copyright 2005 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 
 889                                 public void epilogue() {
 890                                 }
 891                             };
 892                         VM.getVM().getObjectHeap().iterateRaw(iterator);
 893                     } else if (type.equals("codecache")) {
 894                         CodeCacheVisitor v = new CodeCacheVisitor() {
 895                                 public void prologue(Address start, Address end) {
 896                                 }
 897                                 public void visit(CodeBlob blob) {
 898                                     boolean printed = false;
 899                                     Address base = blob.getAddress();
 900                                     Address end = base.addOffsetTo(blob.getSize());
 901                                     while (base != null && base.lessThan(end)) {
 902                                         Address val = base.getAddressAt(0);
 903                                         if (AddressOps.equal(val, value)) {
 904                                             if (!printed) {
 905                                                 printed = true;
 906                                                 blob.printOn(out);
 907                                             }
 908                                             out.println("found at " + base + "\n");
 909                                         }
 910                                         base = base.addOffsetTo(stride);
 911                                     }
 912                                 }
 913                                 public void epilogue() {
 914                                 }
 915 
 916 
 917                             };
 918                         VM.getVM().getCodeCache().iterate(v);
 919 
 920                     }
 921                 }
 922             }
 923         },
 924         new Command("where", "where { -a | id }", false) {
 925             public void doit(Tokens t) {
 926                 if (t.countTokens() != 1) {
 927                     usage();
 928                 } else {
 929                     String name = t.nextToken();
 930                     Threads threads = VM.getVM().getThreads();
 931                     boolean all = name.equals("-a");
 932                     for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
 933                         StringWriter sw = new StringWriter();
 934                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
 935                         thread.printThreadIDOn(new PrintStream(bos));
 936                         if (all || bos.toString().equals(name)) {
 937                             HTMLGenerator gen = new HTMLGenerator(false);
 938                             out.println(gen.genHTMLForJavaStackTrace(thread));
 939                             if (!all) return;
 940                         }
 941                     }
 942                     if (!all) out.println("Couldn't find thread " + name);
 943                 }
 944             }
 945         },
 946 
 947         new Command("threads", false) {
 948             public void doit(Tokens t) {
 949                 if (t.countTokens() != 0) {
 950                     usage();
 951                 } else {
 952                     Threads threads = VM.getVM().getThreads();
 953                     for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
 954                         thread.printThreadIDOn(out);
 955                         out.println(" " + thread.getThreadName());
 956                     }
 957                 }
 958             }
 959         },
 960 
 961         new Command("livenmethods", false) {
 962             public void doit(Tokens t) {
 963                 if (t.countTokens() != 0) {
 964                     usage();
 965                 } else {
 966                     ArrayList nmethods = new ArrayList();
 967                     Threads threads = VM.getVM().getThreads();
 968                     HTMLGenerator gen = new HTMLGenerator(false);
 969                     for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
 970                         try {
 971                             for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) {
 972                                 if (vf instanceof CompiledVFrame) {
 973                                     NMethod c = ((CompiledVFrame)vf).getCode();
 974                                     if (!nmethods.contains(c)) {
 975                                         nmethods.add(c);
 976                                         out.println(gen.genHTML(c));
 977                                     }
 978                                 }
 979                             }
 980                         } catch (Exception e) {
 981                             e.printStackTrace();
 982                         }
 983                     }
 984                 }
 985             }
 986         },
 987         new Command("universe", false) {
 988             public void doit(Tokens t) {
 989                 if (t.countTokens() != 0) {
 990                     usage();
 991                 } else {
 992                     Universe u = VM.getVM().getUniverse();
 993                     out.println("Heap Parameters:");
 994                     u.heap().printOn(out);
 995                 }
 996             }
 997         },
 998         new Command("verbose", "verbose true | false", true) {
 999             public void doit(Tokens t) {
1000                 if (t.countTokens() != 1) {
1001                     usage();
1002                 } else {
1003                     verboseExceptions = Boolean.valueOf(t.nextToken()).booleanValue();
1004                 }
1005             }
1006         },
1007         new Command("assert", "assert true | false", true) {
1008             public void doit(Tokens t) {
1009                 if (t.countTokens() != 1) {
1010                     usage();
1011                 } else {
1012                     Assert.ASSERTS_ENABLED = Boolean.valueOf(t.nextToken()).booleanValue();
1013                 }
1014             }
1015         },
1016     };
1017 
1018     private boolean verboseExceptions = false;
1019     private ArrayList history = new ArrayList();
1020     private HashMap commands = new HashMap();
1021     private boolean doEcho = false;
1022 
1023     private Command findCommand(String key) {
1024         return (Command)commands.get(key);
1025     }
1026 
1027     public void printPrompt() {
1028         out.print("hsdb> ");
1029     }
1030 
1031     private DebuggerInterface debugger;
1032     private HotSpotAgent agent;
1033     private JSJavaScriptEngine jsengine;
1034     private BufferedReader in;
1035     private PrintStream out;
1036     private PrintStream err;
1037 
1038     // called before debuggee attach
1039     private void preAttach() {
1040         // nothing for now..
1041     }
1042 
1043     // called after debuggee attach
1044     private void postAttach() {
1045         // create JavaScript engine and start it
1046         jsengine = new JSJavaScriptEngine() {
1047                         private ObjectReader reader = new ObjectReader();
1048                         private JSJavaFactory factory = new JSJavaFactoryImpl();
1049                         public ObjectReader getObjectReader() {
1050                             return reader;
1051                         }
1052                         public JSJavaFactory getJSJavaFactory() {
1053                             return factory;
1054                         }
1055                         protected void quit() {
1056                             debugger.detach();
1057                             System.exit(0);
1058                         }
1059                         protected BufferedReader getInputReader() {
1060                             return in;
1061                         }
1062                         protected PrintStream getOutputStream() {
1063                             return out;
1064                         }
1065                         protected PrintStream getErrorStream() {
1066                             return err;
1067                         }
1068                    };
1069         try { 
1070             jsengine.defineFunction(this, 
1071                      this.getClass().getMethod("registerCommand",
1072                                 new Class[] { 
1073                                      String.class, String.class, String.class 
1074                                 }));
1075         } catch (NoSuchMethodException exp) {
1076             // should not happen, see below...!!
1077             exp.printStackTrace();
1078         }
1079         jsengine.start();
1080     }
1081 
1082     public void registerCommand(String cmd, String usage, final String func) {
1083         commands.put(cmd, new Command(cmd, usage, false) {
1084                               public void doit(Tokens t) {
1085                                   final int len = t.countTokens();
1086                                   Object[] args = new Object[len];
1087                                   for (int i = 0; i < len; i++) {
1088                                       args[i] = t.nextToken();
1089                                   }
1090                                   jsengine.call(func, args);
1091                               }
1092                           });
1093     }
1094 
1095     public void setOutput(PrintStream o) {
1096         out = o;
1097     }
1098 
1099     public void setErr(PrintStream e) {
1100         err = e;
1101     }
1102 
1103     public CommandProcessor(DebuggerInterface debugger, BufferedReader in, PrintStream out, PrintStream err) {
1104         this.debugger = debugger;
1105         this.agent = debugger.getAgent();
1106         this.in = in;
1107         this.out = out;
1108         this.err = err;
1109         for (int i = 0; i < commandList.length; i++) {
1110             Command c = commandList[i];
1111             commands.put(c.name, c);
1112         }
1113         if (debugger.isAttached()) {
1114             postAttach();
1115         }
1116     }
1117    
1118 
1119     public void run(boolean prompt) {
1120         // Process interactive commands.
1121         while (true) {
1122             if (prompt) printPrompt();
1123             String ln = null;
1124             try {
1125                 ln = in.readLine();
1126             } catch (IOException e) {
1127             }
1128             if (ln == null) {
1129                 if (prompt) err.println("Input stream closed.");
1130                 return;
1131             }
1132     
1133             executeCommand(ln);
1134         }
1135     }
1136 
1137     static Pattern historyPattern = Pattern.compile("((!\\*)|(!\\$)|(!!-?)|(!-?[0-9][0-9]*))");
1138 
1139     public void executeCommand(String ln) {
1140         if (ln.indexOf('!') != -1) {
1141             int size = history.size();
1142             if (size == 0) {
1143                 ln = "";
1144                 err.println("History is empty");
1145             } else {
1146                 StringBuffer result = new StringBuffer();
1147                 Matcher m = historyPattern.matcher(ln);
1148                 int start = 0;
1149                 while (m.find()) {
1150                     if (m.start() > start) {
1151                         result.append(ln.substring(start, m.start() - start));
1152                     }
1153                     start = m.end();
1154 
1155                     String cmd = m.group();
1156                     if (cmd.equals("!!")) {
1157                         result.append((String)history.get(history.size() - 1));
1158                     } else if (cmd.equals("!!-")) {
1159                         Tokens item = new Tokens((String)history.get(history.size() - 1));
1160                         item.trim(1);
1161                         result.append(item.join(" "));
1162                     } else if (cmd.equals("!*")) {
1163                         Tokens item = new Tokens((String)history.get(history.size() - 1));
1164                         item.nextToken();
1165                         result.append(item.join(" "));
1166                     } else if (cmd.equals("!$")) {
1167                         Tokens item = new Tokens((String)history.get(history.size() - 1));
1168                         result.append(item.at(item.countTokens() - 1));
1169                     } else {
1170                         String tail = cmd.substring(1);
1171                         int index = Integer.parseInt(tail);
1172                         if (index < 0) {
1173                             index = history.size() + index;
1174                         }
1175                         if (index > size) {
1176                             err.println("No such history item");
1177                         } else {
1178                             result.append((String)history.get(index));
1179                         }
1180                     }
1181                 }
1182                 if (result.length() == 0) {
1183                     err.println("malformed history reference");
1184                     ln = "";
1185                 } else {
1186                     if (start < ln.length()) {
1187                         result.append(ln.substring(start));
1188                     }
1189                     ln = result.toString();
1190                     if (!doEcho) {
1191                         out.println(ln);
1192                     }
1193                 }
1194             }
1195         }
1196 
1197         if (doEcho) {
1198             out.println("+ " + ln);
1199         }
1200 
1201         PrintStream redirect = null;
1202         Tokens t = new Tokens(ln);
1203         if (t.hasMoreTokens()) {
1204             boolean error = false;
1205             history.add(ln);
1206             int len = t.countTokens();
1207             if (len > 2) {
1208                 String r = t.at(len - 2);
1209                 if (r.equals(">") || r.equals(">>")) {
1210                     boolean append = r.length() == 2;
1211                     String file = t.at(len - 1);
1212                     try {
1213                         redirect = new PrintStream(new BufferedOutputStream(new FileOutputStream(file, append)));
1214                         t.trim(2);
1215                     } catch (Exception e) {
1216                         out.println("Error: " + e);
1217                         if (verboseExceptions) {
1218                             e.printStackTrace(out);
1219                         }
1220                         error = true;
1221                     }
1222                 }
1223             }
1224             if (!error) {
1225                 PrintStream savedout = out;
1226                 if (redirect != null) {
1227                     out = redirect;
1228                 }
1229                 try {
1230                     executeCommand(t);
1231                 } catch (Exception e) {
1232                     err.println("Error: " + e);
1233                     if (verboseExceptions) {
1234                         e.printStackTrace(err);
1235                     }
1236                 } finally {
1237                     if (redirect != null) {
1238                         out = savedout;
1239                         redirect.close();
1240                     }
1241                 }
1242             }
1243         }
1244     }
1245 
1246     void executeCommand(Tokens args) {
1247         String cmd = args.nextToken();
1248 
1249         Command doit = findCommand(cmd);
1250 
1251         /*
1252          * Check for an unknown command
1253          */
1254         if (doit == null) {
1255             out.println("Unrecognized command.  Try help...");
1256         } else if (!debugger.isAttached() && !doit.okIfDisconnected) {
1257             out.println("Command not valid until the attached to a VM");
1258         } else {
1259             try {
1260                 doit.doit(args);
1261             } catch (Exception e) {
1262                 out.println("Error: " + e);
1263                 if (verboseExceptions) {
1264                     e.printStackTrace(out);
1265                 }
1266             }
1267         }
1268     }
1269 }