1 /*
   2  * Copyright 2004-2007 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 // shorter names for SA packages
  26 
  27 
  28 // SA package name abbreviations are kept in 'sapkg' object
  29 // to avoid global namespace pollution
  30 var sapkg = new Object();
  31 
  32 sapkg.hotspot = Packages.sun.jvm.hotspot;
  33 sapkg.asm = sapkg.hotspot.asm;
  34 sapkg.bugspot = sapkg.hotspot.bugspot;
  35 sapkg.c1 = sapkg.hotspot.c1;
  36 sapkg.code = sapkg.hotspot.code;
  37 sapkg.compiler = sapkg.hotspot.compiler;
  38 
  39 // 'debugger' is a JavaScript keyword :-(
  40 // sapkg.debugger = sapkg.hotspot.debugger;
  41 
  42 sapkg.interpreter = sapkg.hotspot.interpreter;
  43 sapkg.livejvm = sapkg.hotspot.livejvm;
  44 sapkg.jdi = sapkg.hotspot.jdi;
  45 sapkg.memory = sapkg.hotspot.memory;
  46 sapkg.oops = sapkg.hotspot.oops;
  47 sapkg.runtime = sapkg.hotspot.runtime;
  48 sapkg.tools = sapkg.hotspot.tools;
  49 sapkg.types = sapkg.hotspot.types;
  50 sapkg.ui = sapkg.hotspot.ui;
  51 sapkg.utilities = sapkg.hotspot.utilities;
  52 
  53 // SA singletons are kept in 'sa' object
  54 var sa = new Object();
  55 sa.vm = sapkg.runtime.VM.getVM();
  56 sa.dbg = sa.vm.getDebugger();
  57 sa.cdbg = sa.dbg.CDebugger;
  58 sa.heap = sa.vm.universe.heap();
  59 sa.systemDictionary = sa.vm.systemDictionary;
  60 sa.sysDict = sa.systemDictionary;
  61 sa.symbolTable = sa.vm.symbolTable;
  62 sa.symTbl = sa.symbolTable;
  63 sa.threads = sa.vm.threads;
  64 sa.interpreter = sa.vm.interpreter;
  65 sa.typedb = sa.vm.typeDataBase;
  66 sa.codeCache = sa.vm.codeCache;
  67 // 'objHeap' is different from 'heap'!. 
  68 // This is SA's Oop factory and heap-walker
  69 sa.objHeap = sa.vm.objectHeap;
  70 
  71 // few useful global variables
  72 var OS = sa.vm.OS;
  73 var CPU = sa.vm.CPU;
  74 var LP64 = sa.vm.LP64;
  75 var isClient = sa.vm.clientCompiler;
  76 var isServer = sa.vm.serverCompiler;
  77 var isCore = sa.vm.isCore();
  78 var addressSize = sa.vm.addressSize;
  79 var oopSize = sa.vm.oopSize;
  80 
  81 // this "main" function is called immediately
  82 // after loading this script file
  83 function main(globals, jvmarg) {
  84   // wrap a sun.jvm.hotspot.utilities.soql.ScriptObject
  85   // object so that the properties of it can be accessed
  86   // in natural object.field syntax.
  87   function wrapScriptObject(so) {
  88     function unwrapScriptObject(wso) {
  89       var objType = typeof(wso);
  90       if ((objType == 'object' ||
  91            objType == 'function')
  92           && "__wrapped__" in wso) {
  93         return wso.__wrapped__;
  94       } else {
  95         return wso;
  96       }
  97     }
  98 
  99     function prepareArgsArray(array) {
 100       var args = new Array(array.length);
 101       for (var a = 0; a < array.length; a++) {
 102         var elem = array[a];
 103         elem = unwrapScriptObject(elem);
 104         if (typeof(elem) == 'function') {
 105           args[a] = new sapkg.utilities.soql.Callable() {
 106             call: function(myargs) {
 107               var tmp = new Array(myargs.length);
 108               for (var i = 0; i < myargs.length; i++) {
 109                 tmp[i] = wrapScriptObject(myargs[i]);
 110               }
 111               return elem.apply(this, tmp);
 112             }
 113           }
 114         } else {
 115           args[a] = elem;
 116         }
 117       }
 118       return args;
 119     }
 120 
 121     if (so instanceof sapkg.utilities.soql.ScriptObject) {
 122       return new JSAdapter() {
 123         __getIds__: function() {                  
 124           return so.getIds();         
 125         },
 126   
 127         __has__ : function(name) {
 128           if (typeof(name) == 'number') {
 129             return so["has(int)"](name);
 130           } else {
 131             if (name == '__wrapped__') {
 132               return true;
 133             } else if (so["has(java.lang.String)"](name)) {
 134               return true;
 135             } else if (name.equals('toString')) {
 136               return true;
 137             } else {
 138               return false;
 139             }
 140           }
 141         },
 142   
 143         __delete__ : function(name) {
 144           if (typeof(name) == 'number') {
 145             return so["delete(int)"](name);
 146           } else {
 147             return so["delete(java.lang.String)"](name);
 148           }
 149         },
 150   
 151         __get__ : function(name) {
 152           if (! this.__has__(name)) {
 153             return undefined;
 154           }
 155           if (typeof(name) == 'number') {
 156             return wrapScriptObject(so["get(int)"](name));
 157           } else {
 158             if (name == '__wrapped__') {
 159               return so;
 160             } else {
 161               var value = so["get(java.lang.String)"](name);
 162               if (value instanceof sapkg.utilities.soql.Callable) {
 163                 return function() {
 164                   var args = prepareArgsArray(arguments);
 165                   var r;
 166                   try {
 167                     r = value.call(args);
 168                   } catch (e) {
 169                     println("call to " + name + " failed!");
 170                     throw e;
 171                   }
 172                   return wrapScriptObject(r);
 173                 }
 174               } else if (name == 'toString') {
 175                 return function() { 
 176                   return so.toString();
 177                 }
 178               } else {
 179                 return wrapScriptObject(value);
 180               }
 181             }
 182           }
 183         }
 184       };
 185     } else {
 186       return so;
 187     }
 188   }
 189 
 190   // set "jvm" global variable that wraps a 
 191   // sun.jvm.hotspot.utilities.soql.JSJavaVM instance
 192   if (jvmarg != null) {
 193     jvm = wrapScriptObject(jvmarg);
 194     // expose "heap" global variable
 195     heap = jvm.heap;
 196   }
 197 
 198   // expose all "function" type properties of
 199   // sun.jvm.hotspot.utilitites.soql.JSJavaScriptEngine
 200   // as global functions here.
 201   globals = wrapScriptObject(globals);
 202   for (var prop in globals) {    
 203     if (typeof(globals[prop]) == 'function') {
 204       this[prop] = globals[prop];
 205     }    
 206   }
 207 
 208   // define "writeln" and "write" if not defined
 209   if (typeof(writeln) == 'undefined') {
 210     writeln = println;
 211   }
 212 
 213   if (typeof(write) == 'undefined') {
 214     write = print;
 215   }
 216 
 217   // "registerCommand" function is defined if we
 218   // are running as part of "CLHSDB" tool. CLHSDB
 219   // tool exposes Unix-style commands. 
 220 
 221   // if "registerCommand" function is defined
 222   // then register few global functions as "commands".
 223   if (typeof(registerCommand) == 'function') {
 224     this.printDis = function(addr, len) {
 225       if (!addr) {
 226          writeln("Usage: dis address [ length ]");
 227       } else {
 228          dis(addr, len);
 229       }
 230     }
 231     registerCommand("dis", "dis address [ length ]", "printDis");
 232 
 233     this.jclass = function(name) {
 234       if (typeof(name) == "string") {
 235          var clazz = sapkg.utilities.SystemDictionaryHelper.findInstanceKlass(name);
 236          if (clazz) {
 237              writeln(clazz.getName().asString() + " @" + clazz.getHandle().toString());
 238          } else {
 239              writeln("class not found: " + name);
 240          } 
 241       } else {
 242          writeln("Usage: class name");
 243       }
 244     }
 245     registerCommand("class", "class name", "jclass");
 246 
 247     this.jclasses = function() {
 248       forEachKlass(function (clazz) {
 249         writeln(clazz.getName().asString() + " @" + clazz.getHandle().toString()); 
 250       });
 251     }
 252     registerCommand("classes", "classes", "jclasses");
 253 
 254     this.printJDis = function(addr) {
 255       if (!addr) {
 256          writeln("Usage: jdis address");
 257       } else {
 258          jdis(addr);
 259       }
 260     }
 261     registerCommand("jdis", "jdis address", "printJDis");
 262 
 263     this.dclass = function(clazz, dir) {
 264       if (!clazz) {
 265          writeln("Usage: dumpclass { address | name } [ directory ]");
 266       } else {
 267          if (!dir) { dir = "."; }
 268          dumpClass(clazz, dir);
 269       }
 270     }
 271     registerCommand("dumpclass", "dumpclass { address | name } [ directory ]", "dclass");
 272     registerCommand("dumpheap", "dumpheap [ file ]", "dumpHeap");
 273 
 274     this.jseval = function(str) {
 275       if (!str) {
 276          writeln("Usage: jseval script");
 277       } else {
 278          var res = eval(str);
 279          if (res) { writeln(res); }
 280       }
 281     }
 282     registerCommand("jseval", "jseval script", "jseval");
 283 
 284     this.jsload = function(file) {
 285       if (!file) {
 286          writeln("Usage: jsload file");
 287       } else {
 288          load(file);
 289       }
 290     }
 291     registerCommand("jsload", "jsload file", "jsload");
 292 
 293     this.printMem = function(addr, len) {
 294       if (!addr) {
 295          writeln("Usage: mem [ length ]");
 296       } else {
 297          mem(addr, len);
 298       }
 299     }
 300     registerCommand("mem", "mem address [ length ]", "printMem");
 301 
 302     this.sysProps = function() {
 303       for (var i in jvm.sysProps) {
 304          writeln(i + ' = ' + jvm.sysProps[i]);
 305       }
 306     }
 307     registerCommand("sysprops", "sysprops", "sysProps");
 308 
 309     this.printWhatis = function(addr) {
 310       if (!addr) {
 311          writeln("Usage: whatis address");
 312       } else {
 313          writeln(whatis(addr));
 314       }
 315     }
 316     registerCommand("whatis", "whatis address", "printWhatis");
 317   }  
 318 }
 319 
 320 // debugger functionality
 321 
 322 // string-to-Address
 323 function str2addr(str) {
 324    return sa.dbg.parseAddress(str);
 325 }
 326 
 327 // number-to-Address
 328 if (addressSize == 4) {
 329    eval("function num2addr(num) { \
 330             return str2addr('0x' + java.lang.Integer.toHexString(0xffffffff & num)); \
 331          }");
 332 } else {
 333    eval("function num2addr(num) { \
 334             return str2addr('0x' + java.lang.Long.toHexString(num));  \
 335          }");
 336 }
 337 
 338 // generic any-type-to-Address
 339 // use this convenience function to accept address in any
 340 // format -- number, string or an Address instance.
 341 function any2addr(addr) {
 342    var type = typeof(addr);
 343    if (type == 'number') {
 344       return num2addr(addr);
 345    } else if (type == 'string') {         
 346       return str2addr(addr);
 347    } else {
 348       return addr;
 349    }
 350 }
 351 
 352 // Address-to-string
 353 function addr2str(addr) {
 354    if (addr == null) {
 355       return (addressSize == 4)? '0x00000000' : '0x0000000000000000';
 356    } else {
 357       return addr + '';
 358    }
 359 }
 360 
 361 // Address-to-number
 362 function addr2num(addr) {
 363    return sa.dbg.getAddressValue(addr);
 364 }
 365 
 366 // symbol-to-Address
 367 function sym2addr(dso, sym) {
 368    return sa.dbg.lookup(dso, sym);
 369 }
 370 
 371 // returns the ClosestSymbol or null
 372 function closestSymbolFor(addr) {
 373    if (sa.cdbg == null) {
 374       // no CDebugger support, return null
 375       return null;
 376    } else {
 377       var dso = sa.cdbg.loadObjectContainingPC(addr);
 378       if (dso != null) {
 379          return dso.closestSymbolToPC(addr);
 380       } else {
 381          return null;
 382       }
 383    }
 384 }
 385 
 386 // Address-to-symbol
 387 // returns nearest symbol as string if found
 388 // else returns address as string
 389 function addr2sym(addr) {
 390     var sym = closestSymbolFor(addr);
 391     if (sym != null)  {
 392        return sym.name + '+' + sym.offset;
 393     } else {
 394        return addr2str(addr);
 395     }
 396 }
 397 
 398 // read 'num' bytes at 'addr' and return an array as result.
 399 // returns Java byte[] type result and not a JavaScript array.
 400 function readBytesAt(addr, num) {
 401    addr = any2addr(addr);
 402    var res = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, num);
 403    var i;
 404    for (i = 0; i < num; i++) {
 405       res[i] = addr.getJByteAt(i);
 406    }
 407    return res;
 408 }
 409 
 410 // read 'num' words at 'addr' and return an array as result.
 411 // returns Java long[] type result and not a JavaScript array.
 412 function readWordsAt(addr, num) {
 413    addr = any2addr(addr);
 414    var res = java.lang.reflect.Array.newInstance(java.lang.Long.TYPE, num);
 415    var i;
 416    for (i = 0; i < num; i++) {
 417       res[i] = addr2num(addr.getAddressAt(i * addressSize));
 418    }
 419    return res;
 420 }
 421 
 422 // read the 'C' string at 'addr'
 423 function readCStrAt(addr) {
 424    addr = any2addr(addr);
 425    return sapkg.utilities.CStringUtilities.getString(addr);
 426 }
 427 
 428 // read the length of the 'C' string at 'addr'
 429 function readCStrLen(addr) {
 430    addr = any2addr(addr);
 431    return sapkg.utilities.CStringUtilities.getStringLength(addr);
 432 }
 433 
 434 // iterate through ThreadList of CDebugger
 435 function forEachThread(callback) {
 436    if (sa.cdbg == null) {
 437       // no CDebugger support
 438       return;
 439    } else {
 440       var itr = sa.cdbg.threadList.iterator();
 441       while (itr.hasNext()) {
 442          if (callback(itr.next()) == false) return;
 443       }
 444    }
 445 }
 446 
 447 // read register set of a ThreadProxy as name-value pairs
 448 function readRegs(threadProxy) {
 449    var ctx = threadProxy.context;
 450    var num = ctx.numRegisters;
 451    var res = new Object();
 452    var i;
 453    for (i = 0; i < num; i++) {
 454       res[ctx.getRegisterName(i)]= addr2str(ctx.getRegisterAsAddress(i));
 455    }
 456    return res;
 457 }
 458 
 459 // print register set for a given ThreaProxy
 460 function regs(threadProxy) {
 461    var res = readRegs(threadProxy);
 462    for (i in res) {
 463       writeln(i, '=', res[i]);
 464    }
 465 }
 466 
 467 // iterate through each CFrame of a given ThreadProxy
 468 function forEachCFrame(threadProxy, callback) {   
 469    if (sa.cdbg == null) {
 470       // no CDebugger support
 471       return;
 472    } else {
 473       var cframe = sa.cdbg.topFrameForThread(threadProxy);
 474       while (cframe != null) {
 475          if (callback(cframe) == false) return;
 476          cframe = cframe.sender();
 477       }
 478    }
 479 }
 480 
 481 // iterate through list of load objects (DLLs, DSOs)
 482 function forEachLoadObject(callback) {
 483    if (sa.cdbg == null) {
 484       // no CDebugger support
 485       return;
 486    } else {
 487       var itr = sa.cdbg.loadObjectList.iterator();
 488       while (itr.hasNext()) {
 489          if (callback(itr.next()) == false) return;
 490       }
 491    }
 492 }
 493 
 494 // print 'num' words at 'addr'
 495 function mem(addr, num) {
 496    if (num == undefined) {
 497       num = 1;
 498    }
 499    addr = any2addr(addr);   
 500    var i;
 501    for (i = 0; i < num; i++) {
 502       var value = addr.getAddressAt(0);      
 503       writeln(addr2sym(addr) + ':', addr2str(value)); 
 504       addr = addr.addOffsetTo(addressSize);      
 505    }
 506    writeln();
 507 }
 508 
 509 // return the disassemble class for current CPU
 510 function disassemblerClass() {
 511    var DisAsmClass;
 512    if (CPU == 'x86') {
 513       DisAsmClass = sapkg.asm.x86.X86Disassembler;
 514    } else if (CPU == 'sparc') {
 515       DisAsmClass = sapkg.asm.sparc.SPARCV9Disassembler;
 516    }
 517    return DisAsmClass;
 518 }
 519 
 520 // print native code disassembly of 'num' bytes at 'addr'
 521 function dis(addr, num) {
 522    addr = any2addr(addr);
 523    var nmethod = findNMethod(addr);
 524    if (nmethod != null) {
 525       // disassemble it as nmethod
 526       nmethoddis(nmethod);     
 527    } else {
 528       // raw disassembly
 529       if (num == undefined) {
 530          // size of one SPARC instruction and
 531          // unknown number of Intel instructions.
 532          num = 4;
 533       }
 534       DisAsmClass = disassemblerClass();
 535       if (DisAsmClass == undefined) {
 536          // unsupported CPU
 537          writeln(CPU + " is not yet supported!");
 538          return;
 539       }
 540 
 541       var bytes = readBytesAt(addr, num);
 542       var disAsm = new DisAsmClass(addr2num(addr), bytes);
 543       disAsm.decode(new sapkg.asm.InstructionVisitor() {
 544                       visit: function (pc, instr) {
 545                          write(addr2sym(num2addr(pc)) + ':', '\t');
 546                          writeln(instr.asString(pc, 
 547                                  new sapkg.asm.SymbolFinder() {
 548                                     getSymbolFor: function(addr) {
 549                                        return addr2sym(num2addr(addr));
 550                                     }
 551                                  }));
 552                       }
 553                    });
 554    }
 555 }
 556 
 557 // System dictionary functions
 558 
 559 // find InstanceKlass by name
 560 function findInstanceKlass(name) {
 561    return sapkg.utilities.SystemDictionaryHelper.findInstanceKlass(name);
 562 }
 563 
 564 // get Java system loader (i.e., application launcher loader)
 565 function systemLoader() {
 566    return sa.sysDict.javaSystemLoader();
 567 }
 568 
 569 // iterate system dictionary for each 'Klass' 
 570 function forEachKlass(callback) {
 571    var VisitorClass = sapkg.memory.SystemDictionary.ClassVisitor;
 572    var visitor = new VisitorClass() { visit: callback };
 573    sa.sysDict["classesDo(sun.jvm.hotspot.memory.SystemDictionary$ClassVisitor)"](visitor);
 574 }
 575 
 576 // iterate system dictionary for each 'Klass' and initiating loader
 577 function forEachKlassAndLoader(callback) {
 578    var VisitorClass = sapkg.memory.SystemDictionary.ClassAndLoaderVisitor;
 579    var visitor = new VisitorClass() { visit: callback };
 580    sa.sysDict["classesDo(sun.jvm.hotspot.memory.SystemDictionary$ClassAndLoaderVisitor)"](visitor);
 581 }
 582 
 583 // iterate system dictionary for each primitive array klass
 584 function forEachPrimArrayKlass(callback) {
 585    var VisitorClass = sapkg.memory.SystemDictionary.ClassAndLoaderVisitor;
 586    sa.sysDict.primArrayClassesDo(new VisitorClass() { visit: callback });
 587 }
 588 
 589 // (hotspot) symbol table functions
 590 
 591 // String-to-Symbol
 592 function str2sym(str) {
 593    return sa.symTbl.probe(str);
 594 }
 595 
 596 // Symbol-to-String
 597 function sym2str(sym) {
 598    return sym.asString();
 599 }
 600 
 601 // oop functions
 602 
 603 // Address-to-Oop
 604 function addr2oop(addr) {
 605    addr = any2addr(addr);
 606    return sa.objHeap.newOop(addr.addOffsetToAsOopHandle(0));
 607 }
 608 
 609 // Oop-to-Address
 610 function oop2addr(oop) {
 611    return oop.handle;
 612 }
 613 
 614 // 'oop' to higher-level java object wrapper in which for(i in o) 
 615 // works by iterating java level fields and javaobject.javafield
 616 // syntax works.
 617 function oop2obj(oop) {
 618    return object(addr2str(oop.handle));
 619 }
 620 
 621 // higher level java object wrapper to oop
 622 function obj2oop(obj) {
 623    return addr2oop(str2addr(address(obj)));
 624 }
 625 
 626 // Java heap iteration
 627 
 628 // iterates Java heap for each Oop
 629 function forEachOop(callback) {
 630    sa.objHeap.iterate(new sapkg.oops.HeapVisitor() { doObj: callback });
 631 }
 632 
 633 // iterates Java heap for each Oop of given 'klass'.
 634 // 'includeSubtypes' tells whether to include objects 
 635 // of subtypes of 'klass' or not
 636 function forEachOopOfKlass(callback, klass, includeSubtypes) {
 637    if (klass == undefined) {
 638        klass = findInstanceKlass("java.lang.Object");
 639    }
 640 
 641    if (includeSubtypes == undefined) {
 642       includeSubtypes = true;
 643    }
 644    sa.objHeap.iterateObjectsOfKlass(
 645         new sapkg.oops.HeapVisitor() { doObj: callback },
 646         klass, includeSubtypes);
 647 }
 648 
 649 // code cache functions
 650 
 651 // iterates CodeCache for each 'CodeBlob'
 652 function forEachCodeBlob(callback) {
 653    var VisitorClass = sapkg.code.CodeCacheVisitor;
 654    sa.codeCache.iterate(new VisitorClass() { visit: callback });
 655 }
 656 
 657 // find the ClodBlob (if any) that contains given address
 658 function findCodeBlob(addr) {
 659    addr = any2addr(addr);
 660    return sa.codeCache.findBlobUnsafe(addr);
 661 }
 662 
 663 // find the NMethod (if any) that contains given address
 664 function findNMethod(addr) {
 665    var codeBlob = findCodeBlob(addr);
 666    return (codeBlob != null && codeBlob.isNMethod())? codeBlob : null;
 667 }
 668 
 669 // returns PcDesc at given address or null
 670 function pcDescAt(addr) {
 671    addr = any2addr(addr);
 672    var nmethod = findNMethod(addr);
 673    return (nmethod != null)? nmethod.safepoints.get(addr) : null;
 674 }
 675 
 676 // helpers for nmethod disassembler
 677 function printScope(scopeDesc) {
 678    if (scopeDesc == null) {
 679       return;
 680    }
 681    printScope(scopeDesc.sender());
 682    var method = scopeDesc.method;
 683    var bci = scopeDesc.BCI;
 684    var line = -1;
 685    if (method.hasLineNumberTable()) {
 686       line = method.getLineNumberFromBCI(bci);
 687    }
 688   
 689    write('\t', method.externalNameAndSignature(), '@', method.handle, 'bci=' + bci);
 690    if (line != -1) { 
 691       write('line=' + line); 
 692    }
 693    writeln();
 694 }
 695 
 696 function printSafepointInfo(nmethod, pcDesc) {
 697    var scopeDesc = nmethod.getScopeDescAt(
 698                       pcDesc.getRealPC(nmethod),
 699                       pcDesc.isAtCall());
 700    printScope(scopeDesc);
 701 }
 702 
 703 // print disassembly for a given nmethod
 704 function nmethoddis(nmethod) {
 705    var DisAsmClass = disassemblerClass();
 706    if (DisAsmClass == undefined) {
 707       writeln(CPU + " is not yet supported!");
 708       return;
 709    }
 710 
 711    var method = nmethod.method;
 712    writeln('NMethod:', method.externalNameAndSignature(), '@', method.handle);
 713 
 714    var codeBegin = nmethod.codeBegin();
 715    var codeEnd = nmethod.codeEnd();
 716    var size = codeEnd.minus(codeBegin);
 717    var code = readBytesAt(codeBegin, size);
 718    var startPc = addr2num(codeBegin);
 719    var verifiedEntryPoint = addr2num(nmethod.verifiedEntryPoint);
 720    var entryPoint = addr2num(nmethod.entryPoint);
 721    var interpreterEntryPoint = addr2num(nmethod.interpreterEntryPointOrNull);
 722    var safepoints = nmethod.safepoints;
 723    var disAsm = new DisAsmClass(startPc, code);
 724    disAsm.decode(new sapkg.asm.InstructionVisitor() {
 725                     visit: function(curPc, instr) {
 726                        if (curPc == verifiedEntryPoint) {
 727                           writeln();                                    
 728                           writeln("Verified Entry Point:");
 729                        }
 730                        if (curPc == entryPoint) {
 731                           writeln();
 732                           writeln("Entry Point:");                     
 733                        }
 734                        if (curPc == interpreterEntryPoint) {
 735                           writeln("");
 736                           writeln("Interpreter Entry Point:");
 737                        }
 738 
 739                        var pcDesc = safepoints.get(num2addr(curPc));
 740                        var isSafepoint = (pcDesc != null);
 741                        if (isSafepoint && pcDesc.isAtCall()) {
 742                           printSafepointInfo(nmethod, pcDesc);
 743                        }
 744 
 745                        write(num2addr(curPc) + ':', '\t');
 746                        writeln(instr.asString(curPc, 
 747                                  new sapkg.asm.SymbolFinder() {
 748                                     getSymbolFor: function(addr) {
 749                                        return addr2sym(num2addr(addr));
 750                                     }
 751                                  }));
 752 
 753                        if (isSafepoint && !pcDesc.isAtCall()) {
 754                           printSafepointInfo(nmethod, pcDesc);
 755                        }
 756                     }                    
 757                  });
 758 }
 759 
 760 // bytecode interpreter functions
 761 
 762 // iterates interpreter codelets for each interpreter codelet
 763 function forEachInterpCodelet(callback) {
 764    var stubQueue = sa.interpreter.code;
 765    var stub = stubQueue.first;
 766    while (stub != null) {
 767       if (callback(stub) == false) return;
 768       stub = stubQueue.getNext(stub);
 769    }
 770 }
 771 
 772 // helper for bytecode disassembler
 773 function printExceptionTable(method) {
 774    var expTbl = method.getExceptionTable();
 775    var len = expTbl.getLength();
 776    if (len != 0) {     
 777       var i;
 778       var cpool = method.constants;
 779       writeln("start", '\t', "end", '\t', "handler", '\t', "exception");
 780       writeln("");
 781       for (i = 0; i < len; i += 4) {
 782          write(expTbl.getIntAt(i), '\t', 
 783                expTbl.getIntAt(i + 1), '\t', 
 784                expTbl.getIntAt(i + 2), '\t');
 785          var cpIndex = expTbl.getIntAt(i + 3);
 786          var oop = (cpIndex == 0)? null : cpool.getObjAt(cpIndex);
 787          if (oop == null) {
 788             writeln("<any>");
 789          } else if (oop.isSymbol()) {
 790             writeln(oop.asString().replace('/', '.'));
 791          } else if (oop.isKlass()) {
 792             writeln(oop.name.asString().replace('/', '.'));
 793          } else {
 794             writeln(cpIndex);
 795          }
 796       }
 797    }
 798 }
 799 
 800 // print Java bytecode disassembly
 801 function jdis(method) {   
 802    if (method.getByteCode == undefined) {
 803       // method oop may be specified by address
 804       method = addr2oop(any2addr(method));
 805    }
 806    writeln(method, '-', method.externalNameAndSignature());
 807    if (method.isNative()) {
 808       writeln("native method");
 809       return;
 810    }
 811    if (method.isAbstract()) {
 812       writeln("abstract method");
 813       return;
 814    }
 815    
 816    writeln();
 817    var BytecodeDisAsmClass = sapkg.interpreter.BytecodeDisassembler;
 818    var disAsm = new BytecodeDisAsmClass(method);
 819    var bci = 0;
 820    var hasLines = method.hasLineNumberTable();
 821    if (hasLines) {
 822       writeln("bci", '\t', "line", '\t', "instruction");
 823    } else {
 824       writeln("bci", '\t', "instruction");
 825    }
 826    writeln("");
 827    disAsm.decode(new sapkg.interpreter.BytecodeVisitor() {
 828                     visit: function(bytecode) {
 829                        if (hasLines) {
 830                           var line = method.getLineNumberFromBCI(bci);
 831                           writeln(bci, '\t', line, '\t', bytecode);
 832                        } else {
 833                           writeln(bci, '\t', bytecode);
 834                        }
 835                        bci++;
 836                     }
 837                  });
 838 
 839     writeln();
 840     printExceptionTable(method);
 841 }
 842 
 843 // Java thread
 844 
 845 // iterates each Thread
 846 function forEachJavaThread(callback) {
 847    var threads = sa.threads;
 848    var thread = threads.first();
 849    while (thread != null) {
 850       if (callback(thread) == false) return;
 851       thread = thread.next();
 852    }  
 853 }
 854 
 855 // iterate Frames of a given thread
 856 function forEachFrame(javaThread, callback) {
 857    var fr = javaThread.getLastFrameDbg();
 858    while (fr != null) { 
 859      if (callback(fr) == false) return;
 860      fr = fr.sender();
 861    }
 862 }
 863 
 864 // iterate JavaVFrames of a given JavaThread
 865 function forEachVFrame(javaThread, callback) {
 866    var vfr = javaThread.getLastJavaVFrameDbg();
 867    while (vfr != null) {
 868       if (callback(vfr) == false) return;
 869       vfr = vfr.javaSender();
 870    }
 871 }
 872 
 873 function printStackTrace(javaThread) {
 874    write("Thread ");
 875    javaThread.printThreadIDOn(java.lang.System.out);
 876    writeln();
 877    forEachVFrame(javaThread, function (vf) {
 878       var method = vf.method;
 879       write(' - ', method.externalNameAndSignature(), '@bci =', vf.getBCI());
 880       var line = method.getLineNumberFromBCI(vf.getBCI());
 881       if (line != -1) { write(', line=', line); }
 882       if (vf.isCompiledFrame()) { write(" (Compiled Frame)"); }
 883       if (vf.isInterpretedFrame()) { write(" (Interpreted Frame)"); }
 884       writeln();
 885    });
 886    writeln();
 887    writeln();
 888 }
 889 
 890 // print Java stack trace for all threads
 891 function where(javaThread) {
 892    if (javaThread == undefined) {
 893       forEachJavaThread(function (jt) { printStackTrace(jt); });
 894    } else {
 895       printStackTrace(javaThread);
 896    }
 897 }
 898 
 899 // vmStructs access -- type database functions
 900 
 901 // find a VM type
 902 function findVMType(typeName) {
 903    return sa.typedb.lookupType(typeName);
 904 }
 905 
 906 // iterate VM types
 907 function forEachVMType(callback) {
 908    var itr = sa.typedb.types;
 909    while (itr.hasNext()) {
 910       if (callback(itr.next()) == false) return;
 911    }
 912 }
 913 
 914 // find VM int constant
 915 function findVMIntConst(name) {
 916    return sa.typedb.lookupIntConstant(name);
 917 }
 918 
 919 // find VM long constant
 920 function findVMLongConst(name) {
 921    return sa.typedb.lookupLongConstant(name);
 922 }
 923 
 924 // iterate VM int constants
 925 function forEachVMIntConst(callback) {
 926    var itr = sa.typedb.intConstants;
 927    while (itr.hasNext()) {
 928       if (callback(itr.next()) == false) return;
 929    } 
 930 }
 931 
 932 // iterate VM long constants
 933 function forEachVMLongConst(callback) {
 934    var itr = sa.typedb.longConstants;
 935    while (itr.hasNext()) {
 936       if (callback(itr.next()) == false) return;
 937    } 
 938 }
 939 
 940 // returns VM Type at address
 941 function vmTypeof(addr) {
 942    addr = any2addr(addr);
 943    return sa.typedb.guessTypeForAddress(addr);
 944 }
 945 
 946 // does the given 'addr' points to an object of given 'type'?
 947 // OR any valid Type at all (if type is undefined)
 948 function isOfVMType(addr, type) {
 949    addr = any2addr(addr);
 950    if (type == undefined) {
 951       return vmTypeof(addr) != null;
 952    } else {
 953       if (typeof(type) == 'string') {
 954          type = findVMType(type);
 955       } 
 956       return sa.typedb.addressTypeIsEqualToType(addr, type);
 957    }
 958 }
 959 
 960 // reads static field value
 961 function readVMStaticField(field) {
 962    var type = field.type;
 963    if (type.isCIntegerType() || type.isJavaPrimitiveType()) {
 964       return field.value;
 965    } else if (type.isPointerType()) {
 966       return field.address;
 967    } else if (type.isOopType()) {
 968       return field.oopHandle;      
 969    } else {
 970       return field.staticFieldAddress;
 971    }
 972 }
 973 
 974 // reads given instance field of VM object at 'addr'
 975 function readVMInstanceField(field, addr) {
 976    var type = field.type;
 977    if (type.isCIntegerType() || type.isJavaPrimitiveType()) {
 978       return field.getValue(addr);
 979    } else if (type.isPointerType()) {
 980       return field.getAddress(addr);
 981    } else if (type.isOopType()) {
 982       return field.getOopHandle(addr);
 983    } else {
 984       return addr.addOffsetTo(field.offset);
 985    }
 986 }
 987 
 988 // returns name-value of pairs of VM type at given address.
 989 // If address is unspecified, reads static fields as name-value pairs.
 990 function readVMType(type, addr) {
 991    if (typeof(type) == 'string') {
 992       type = findVMType(type);
 993    }
 994    if (addr != undefined) {
 995       addr = any2addr(addr);
 996    }
 997 
 998    var result = new Object();
 999    var staticOnly = (addr == undefined);
1000    while (type != null) {
1001       var itr = type.fields;
1002       while (itr.hasNext()) {
1003          var field = itr.next();
1004          var isStatic = field.isStatic();
1005          if (staticOnly && isStatic) {
1006             result[field.name] = readVMStaticField(field);
1007          } else if (!staticOnly && !isStatic) {
1008             result[field.name] = readVMInstanceField(field, addr);
1009          }
1010       }
1011       type = type.superclass;
1012    } 
1013    return result;
1014 }
1015 
1016 function printVMType(type, addr) {
1017    if (typeof(type) == 'string') {
1018       type = findVMType(type);
1019    }
1020    var obj = readVMType(type, addr);
1021    while (type != null) {
1022       var itr = type.fields;
1023       while (itr.hasNext()) {
1024          var field = itr.next();
1025          var name = field.name;
1026          var value = obj[name];
1027          if (value != undefined) {
1028             writeln(field.type.name, type.name + '::' + name, '=', value);
1029          }
1030       }
1031       type = type.superclass;  
1032    }
1033 }
1034 
1035 // define readXXX and printXXX functions for each VM struct/class Type
1036 tmp = new Object();
1037 tmp.itr = sa.typedb.types;
1038 while (tmp.itr.hasNext()) {
1039    tmp.type = tmp.itr.next();
1040    tmp.name = tmp.type.name;
1041    if (tmp.type.isPointerType() || tmp.type.isOopType() ||
1042       tmp.type.isCIntegerType() || tmp.type.isJavaPrimitiveType() ||
1043       tmp.name.equals('address') ||
1044       tmp.name.equals("<opaque>")) {
1045          // ignore;
1046          continue;
1047    } else {
1048       // some type names have ':'. replace to make it as a 
1049       // JavaScript identifier
1050       tmp.name = tmp.name.replace(':', '_').replace('<', '_').replace('>', '_').replace('*', '_').replace(' ', '_');
1051       eval("function read" + tmp.name + "(addr) {" +
1052            "   return readVMType('" + tmp.name + "', addr);}"); 
1053       eval("function print" + tmp.name + "(addr) {" + 
1054            "   printVMType('" + tmp.name + "', addr); }");
1055 
1056       /* FIXME: do we need this?
1057       if (typeof(registerCommand) != 'undefined') {
1058           var name = "print" + tmp.name;
1059           registerCommand(name, name + " [address]", name);
1060       }
1061       */
1062    }
1063 }
1064 //clean-up the temporary
1065 delete tmp;
1066 
1067 // VMObject factory
1068 
1069 // VM type to SA class map
1070 var  vmType2Class = new Object();
1071 
1072 // This is *not* exhaustive. Add more if needed.
1073 // code blobs
1074 vmType2Class["BufferBlob"] = sapkg.code.BufferBlob;
1075 vmType2Class["nmethod"] = sapkg.code.NMethod;
1076 vmType2Class["RuntimeStub"] = sapkg.code.RuntimeStub;
1077 vmType2Class["SafepointBlob"] = sapkg.code.SafepointBlob;
1078 vmType2Class["C2IAdapter"] = sapkg.code.C2IAdapter;
1079 vmType2Class["DeoptimizationBlob"] = sapkg.code.DeoptimizationBlob;
1080 vmType2Class["ExceptionBlob"] = sapkg.code.ExceptionBlob;
1081 vmType2Class["I2CAdapter"] = sapkg.code.I2CAdapter;
1082 vmType2Class["OSRAdapter"] = sapkg.code.OSRAdapter;
1083 vmType2Class["UncommonTrapBlob"] = sapkg.code.UncommonTrapBlob;
1084 vmType2Class["PCDesc"] = sapkg.code.PCDesc;
1085 
1086 // interpreter
1087 vmType2Class["InterpreterCodelet"] = sapkg.interpreter.InterpreterCodelet;
1088 
1089 // Java Threads
1090 vmType2Class["JavaThread"] = sapkg.runtime.JavaThread;
1091 vmType2Class["CompilerThread"] = sapkg.runtime.CompilerThread;
1092 vmType2Class["SurrogateLockerThread"] = sapkg.runtime.JavaThread;
1093 vmType2Class["DebuggerThread"] = sapkg.runtime.DebuggerThread;
1094 
1095 // gc
1096 vmType2Class["GenCollectedHeap"] = sapkg.memory.GenCollectedHeap;
1097 vmType2Class["CompactingPermGenGen"] = sapkg.memory.CompactingPermGenGen;
1098 vmType2Class["DefNewGeneration"] = sapkg.memory.DefNewGeneration;
1099 vmType2Class["TenuredGeneration"] = sapkg.memory.TenuredGeneration;
1100 
1101 // generic VMObject factory for a given address
1102 // This is equivalent to VirtualConstructor.
1103 function newVMObject(addr) {
1104    addr = any2addr(addr);
1105    var result = null;
1106    forEachVMType(function (type) {
1107                     if (isOfVMType(addr, type)) {
1108                        var clazz = vmType2Class[type.name];
1109                        if (clazz != undefined) {
1110                           result = new clazz(addr);
1111                        }
1112                        return false;
1113                     } else {
1114                        return true;
1115                     }
1116                  });
1117    return result;
1118 }
1119 
1120 function vmobj2addr(vmobj) {
1121    return vmobj.address;
1122 }
1123 
1124 function addr2vmobj(addr) {
1125    return newVMObject(addr);
1126 }     
1127 
1128 // Miscellaneous utilities
1129 
1130 // returns PointerLocation that describes the given pointer
1131 function findPtr(addr) {
1132    addr = any2addr(addr);
1133    return sapkg.utilities.PointerFinder.find(addr);
1134 }
1135 
1136 // is given address a valid Oop?
1137 function isOop(addr) {
1138    addr = any2addr(addr);
1139    var oopHandle = addr.addOffsetToAsOopHandle(0);
1140    return sapkg.utilities.RobustOopDeterminator.oopLooksValid(oopHandle);
1141 }
1142 
1143 // returns description of given pointer as a String
1144 function whatis(addr) {
1145    addr = any2addr(addr);
1146    var ptrLoc = findPtr(addr);
1147    if (ptrLoc.isUnknown()) {
1148       var vmType = vmTypeof(addr);
1149       if (vmType != null) {
1150          return "pointer to " + vmType.name;
1151       } else {
1152          var sym = closestSymbolFor(addr);
1153          if (sym != null) {
1154             return sym.name + '+' + sym.offset;
1155          } else {
1156             return ptrLoc.toString();
1157          }
1158       }
1159    } else {
1160       return ptrLoc.toString();
1161    }
1162 }