1 /*
   2  * Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *  
  23  */
  24 
  25 // 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.c1 = sapkg.hotspot.c1;
  35 sapkg.code = sapkg.hotspot.code;
  36 sapkg.compiler = sapkg.hotspot.compiler;
  37 
  38 // 'debugger' is a JavaScript keyword, but ES5 relaxes the
  39 // restriction of using keywords as property name
  40 sapkg.debugger = sapkg.hotspot.debugger;
  41 
  42 sapkg.interpreter = sapkg.hotspot.interpreter;
  43 sapkg.jdi = sapkg.hotspot.jdi;
  44 sapkg.memory = sapkg.hotspot.memory;
  45 sapkg.oops = sapkg.hotspot.oops;
  46 sapkg.runtime = sapkg.hotspot.runtime;
  47 sapkg.tools = sapkg.hotspot.tools;
  48 sapkg.types = sapkg.hotspot.types;
  49 sapkg.ui = sapkg.hotspot.ui;
  50 sapkg.utilities = sapkg.hotspot.utilities;
  51 
  52 // SA singletons are kept in 'sa' object
  53 var sa = new Object();
  54 sa.vm = sapkg.runtime.VM.getVM();
  55 sa.dbg = sa.vm.getDebugger();
  56 sa.cdbg = sa.dbg.CDebugger;
  57 sa.heap = sa.vm.universe.heap();
  58 sa.systemDictionary = sa.vm.systemDictionary;
  59 sa.sysDict = sa.systemDictionary;
  60 sa.symbolTable = sa.vm.symbolTable;
  61 sa.symTbl = sa.symbolTable;
  62 sa.threads = sa.vm.threads;
  63 sa.interpreter = sa.vm.interpreter;
  64 sa.typedb = sa.vm.typeDataBase;
  65 sa.codeCache = sa.vm.codeCache;
  66 // 'objHeap' is different from 'heap'!. 
  67 // This is SA's Oop factory and heap-walker
  68 sa.objHeap = sa.vm.objectHeap;
  69 
  70 // few useful global variables
  71 var OS = sa.vm.OS;
  72 var CPU = sa.vm.CPU;
  73 var LP64 = sa.vm.LP64;
  74 var isClient = sa.vm.clientCompiler;
  75 var isServer = sa.vm.serverCompiler;
  76 var isCore = sa.vm.isCore();
  77 var addressSize = sa.vm.addressSize;
  78 var oopSize = sa.vm.oopSize;
  79 
  80 // this "main" function is called immediately
  81 // after loading this script file
  82 function main(globals, jvmarg) {
  83   // wrap a sun.jvm.hotspot.utilities.soql.ScriptObject
  84   // object so that the properties of it can be accessed
  85   // in natural object.field syntax.
  86   function wrapScriptObject(so) {
  87     function unwrapScriptObject(wso) {
  88       var objType = typeof(wso);
  89       if ((objType == 'object' ||
  90            objType == 'function')
  91           && "__wrapped__" in wso) {
  92         return wso.__wrapped__;
  93       } else {
  94         return wso;
  95       }
  96     }
  97 
  98     function prepareArgsArray(array) {
  99       var args = new Array(array.length);
 100       for (var a = 0; a < array.length; a++) {
 101         var elem = array[a];
 102         elem = unwrapScriptObject(elem);
 103         if (typeof(elem) == 'function') {
 104           args[a] = new sapkg.utilities.soql.Callable() {
 105             call: function(myargs) {
 106               var tmp = new Array(myargs.length);
 107               for (var i = 0; i < myargs.length; i++) {
 108                 tmp[i] = wrapScriptObject(myargs[i]);
 109               }
 110               return elem.apply(this, tmp);
 111             }
 112           }
 113         } else {
 114           args[a] = elem;
 115         }
 116       }
 117       return args;
 118     }
 119 
 120     // Handle __has__ specially to avoid metacircularity problems
 121     // when called from __get__.
 122     // Calling
 123     //   this.__has__(name)
 124     // will in turn call
 125     //   this.__call__('__has__', name)
 126     // which is not handled below
 127     function __has__(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     if (so instanceof sapkg.utilities.soql.ScriptObject) {
 144       return new JSAdapter() {
 145         __getIds__: function() {
 146           return so.getIds();
 147         },
 148   
 149         __has__ : __has__,
 150   
 151         __delete__ : function(name) {
 152           if (typeof(name) == 'number') {
 153             return so["delete(int)"](name);
 154           } else {
 155             return so["delete(java.lang.String)"](name);
 156           }
 157         },
 158   
 159         __get__ : function(name) {
 160               // don't call this.__has__(name); see comments above function __has__
 161           if (! __has__.call(this, name)) {
 162             return undefined;
 163           }
 164           if (typeof(name) == 'number') {
 165             return wrapScriptObject(so["get(int)"](name));
 166           } else {
 167             if (name == '__wrapped__') {
 168               return so;
 169             } else {
 170               var value = so["get(java.lang.String)"](name);
 171               if (value instanceof sapkg.utilities.soql.Callable) {
 172                 return function() {
 173                   var args = prepareArgsArray(arguments);
 174                   var r;
 175                   try {
 176                     r = value.call(Java.to(args, 'java.lang.Object[]'));
 177                   } catch (e) {
 178                     println("call to " + name + " failed!");
 179                     throw e;
 180                   }
 181                   return wrapScriptObject(r);
 182                 }
 183               } else if (name == 'toString') {
 184                 return function() { 
 185                   return so.toString();
 186                 }
 187               } else {
 188                 return wrapScriptObject(value);
 189               }
 190             }
 191           }
 192         }
 193       };
 194     } else {
 195       return so;
 196     }
 197   }
 198 
 199   // set "jvm" global variable that wraps a 
 200   // sun.jvm.hotspot.utilities.soql.JSJavaVM instance
 201   if (jvmarg != null) {
 202     jvm = wrapScriptObject(jvmarg);
 203     // expose "heap" global variable
 204     heap = jvm.heap;
 205   }
 206 
 207   // expose all "function" type properties of
 208   // sun.jvm.hotspot.utilitites.soql.JSJavaScriptEngine
 209   // as global functions here.
 210   globals = wrapScriptObject(globals);
 211   for (var prop in globals) {    
 212     if (typeof(globals[prop]) == 'function') {
 213       this[prop] = globals[prop];
 214     }    
 215   }
 216 
 217   // define "writeln" and "write" if not defined
 218   if (typeof(println) == 'undefined') {
 219     println = function (str) {
 220       java.lang.System.out.println(String(str));
 221     }
 222   }
 223 
 224   if (typeof(print) == 'undefined') {
 225     print = function (str) {
 226       java.lang.System.out.print(String(str));
 227     }
 228   }
 229 
 230   if (typeof(writeln) == 'undefined') {
 231     writeln = println;
 232   }
 233 
 234   if (typeof(write) == 'undefined') {
 235     write = print;
 236   }
 237 
 238   // "registerCommand" function is defined if we
 239   // are running as part of "CLHSDB" tool. CLHSDB
 240   // tool exposes Unix-style commands. 
 241 
 242   // if "registerCommand" function is defined
 243   // then register few global functions as "commands".
 244   if (typeof(registerCommand) == 'function') {
 245     this.jclass = function(name) {
 246       if (typeof(name) == "string") {
 247          var clazz = sapkg.utilities.SystemDictionaryHelper.findInstanceKlass(name);
 248          if (clazz) {
 249              writeln(clazz.getName().asString() + " @" + clazz.getAddress().toString());
 250          } else {
 251              writeln("class not found: " + name);
 252          } 
 253       } else {
 254          writeln("Usage: class name");
 255       }
 256     }
 257     registerCommand("class", "class name", "jclass");
 258 
 259     this.jclasses = function() {
 260       forEachKlass(function (clazz) {
 261         writeln(clazz.getName().asString() + " @" + clazz.getAddress().toString()); 
 262       });
 263     }
 264     registerCommand("classes", "classes", "jclasses");
 265 
 266     this.dclass = function(clazz, dir) {
 267       if (!clazz) {
 268          writeln("Usage: dumpclass { address | name } [ directory ]");
 269       } else {
 270          if (!dir) { dir = "."; }
 271          dumpClass(clazz, dir);
 272       }
 273     }
 274     registerCommand("dumpclass", "dumpclass { address | name } [ directory ]", "dclass");
 275     registerCommand("dumpheap", "dumpheap [ file ]", "dumpHeap");
 276 
 277     this.jseval = function(str) {
 278       if (!str) {
 279          writeln("Usage: jseval script");
 280       } else {
 281          var res = eval(str);
 282          if (res) { writeln(res); }
 283       }
 284     }
 285     registerCommand("jseval", "jseval script", "jseval");
 286 
 287     this.jsload = function(file) {
 288       if (!file) {
 289          writeln("Usage: jsload file");
 290       } else {
 291          load(file);
 292       }
 293     }
 294     registerCommand("jsload", "jsload file", "jsload");
 295 
 296     this.printMem = function(addr, len) {
 297       if (!addr) {
 298          writeln("Usage: mem [ length ]");
 299       } else {
 300          mem(addr, len);
 301       }
 302     }
 303     registerCommand("mem", "mem address [ length ]", "printMem");
 304 
 305     this.sysProps = function() {
 306       for (var i in jvm.sysProps) {
 307          writeln(i + ' = ' + jvm.sysProps[i]);
 308       }
 309     }
 310     registerCommand("sysprops", "sysprops", "sysProps");
 311 
 312     this.printWhatis = function(addr) {
 313       if (!addr) {
 314          writeln("Usage: whatis address");
 315       } else {
 316          writeln(whatis(addr));
 317       }
 318     }
 319     registerCommand("whatis", "whatis address", "printWhatis");
 320   }  
 321 }
 322 
 323 // debugger functionality
 324 
 325 // string-to-Address
 326 function str2addr(str) {
 327    return sa.dbg.parseAddress(str);
 328 }
 329 
 330 // number-to-Address
 331 if (addressSize == 4) {
 332    eval("function num2addr(num) { \
 333             return str2addr('0x' + java.lang.Integer.toHexString(0xffffffff & num)); \
 334          }");
 335 } else {
 336    eval("function num2addr(num) { \
 337             return str2addr('0x' + java.lang.Long.toHexString(num));  \
 338          }");
 339 }
 340 
 341 // generic any-type-to-Address
 342 // use this convenience function to accept address in any
 343 // format -- number, string or an Address instance.
 344 function any2addr(addr) {
 345    var type = typeof(addr);
 346    if (type == 'number') {
 347       return num2addr(addr);
 348    } else if (type == 'string') {         
 349       return str2addr(addr);
 350    } else {
 351       return addr;
 352    }
 353 }
 354 
 355 // Address-to-string
 356 function addr2str(addr) {
 357    if (addr == null) {
 358       return (addressSize == 4)? '0x00000000' : '0x0000000000000000';
 359    } else {
 360       return addr + '';
 361    }
 362 }
 363 
 364 // Address-to-number
 365 function addr2num(addr) {
 366    return sa.dbg.getAddressValue(addr);
 367 }
 368 
 369 // symbol-to-Address
 370 function sym2addr(dso, sym) {
 371    return sa.dbg.lookup(dso, sym);
 372 }
 373 
 374 function loadObjectContainingPC(addr) {
 375     if (sa.cdbg == null) {
 376       // no CDebugger support, return null
 377       return null;
 378     }
 379 
 380     return  sa.cdbg.loadObjectContainingPC(addr);
 381 }
 382 
 383 // returns the ClosestSymbol or null
 384 function closestSymbolFor(addr) {
 385     var dso = loadObjectContainingPC(addr);
 386     if (dso != null) {
 387       return dso.closestSymbolToPC(addr);
 388     }
 389 
 390     return null;
 391 }
 392 
 393 // Address-to-symbol
 394 // returns nearest symbol as string if found
 395 // else returns address as string
 396 function addr2sym(addr) {
 397     var sym = closestSymbolFor(addr);
 398     if (sym != null)  {
 399        return sym.name + '+' + sym.offset;
 400     } else {
 401        return addr2str(addr);
 402     }
 403 }
 404 
 405 // read 'num' words at 'addr' and return an array as result.
 406 // returns Java long[] type result and not a JavaScript array.
 407 function readWordsAt(addr, num) {
 408    addr = any2addr(addr);
 409    var res = java.lang.reflect.Array.newInstance(java.lang.Long.TYPE, num);
 410    var i;
 411    for (i = 0; i < num; i++) {
 412       res[i] = addr2num(addr.getAddressAt(i * addressSize));
 413    }
 414    return res;
 415 }
 416 
 417 // read the 'C' string at 'addr'
 418 function readCStrAt(addr) {
 419    addr = any2addr(addr);
 420    return sapkg.utilities.CStringUtilities.getString(addr);
 421 }
 422 
 423 // read the length of the 'C' string at 'addr'
 424 function readCStrLen(addr) {
 425    addr = any2addr(addr);
 426    return sapkg.utilities.CStringUtilities.getStringLength(addr);
 427 }
 428 
 429 // iterate through ThreadList of CDebugger
 430 function forEachThread(callback) {
 431    if (sa.cdbg == null) {
 432       // no CDebugger support
 433       return;
 434    } else {
 435       var itr = sa.cdbg.threadList.iterator();
 436       while (itr.hasNext()) {
 437          if (callback(itr.next()) == false) return;
 438       }
 439    }
 440 }
 441 
 442 // read register set of a ThreadProxy as name-value pairs
 443 function readRegs(threadProxy) {
 444    var ctx = threadProxy.context;
 445    var num = ctx.numRegisters;
 446    var res = new Object();
 447    var i;
 448    for (i = 0; i < num; i++) {
 449       res[ctx.getRegisterName(i)]= addr2str(ctx.getRegisterAsAddress(i));
 450    }
 451    return res;
 452 }
 453 
 454 // print register set for a given ThreaProxy
 455 function regs(threadProxy) {
 456    var res = readRegs(threadProxy);
 457    for (i in res) {
 458       writeln(i, '=', res[i]);
 459    }
 460 }
 461 
 462 // iterate through each CFrame of a given ThreadProxy
 463 function forEachCFrame(threadProxy, callback) {   
 464    if (sa.cdbg == null) {
 465       // no CDebugger support
 466       return;
 467    } else {
 468       var cframe = sa.cdbg.topFrameForThread(threadProxy);
 469       while (cframe != null) {
 470          if (callback(cframe) == false) return;
 471          cframe = cframe.sender();
 472       }
 473    }
 474 }
 475 
 476 // iterate through list of load objects (DLLs, DSOs)
 477 function forEachLoadObject(callback) {
 478    if (sa.cdbg == null) {
 479       // no CDebugger support
 480       return;
 481    } else {
 482       var itr = sa.cdbg.loadObjectList.iterator();
 483       while (itr.hasNext()) {
 484          if (callback(itr.next()) == false) return;
 485       }
 486    }
 487 }
 488 
 489 // print 'num' words at 'addr'
 490 function mem(addr, num) {
 491    if (num == undefined) {
 492       num = 1;
 493    }
 494    addr = any2addr(addr);   
 495    var i;
 496    for (i = 0; i < num; i++) {
 497       var value = addr.getAddressAt(0);      
 498       writeln(addr2sym(addr) + ':', addr2str(value)); 
 499       addr = addr.addOffsetTo(addressSize);      
 500    }
 501    writeln();
 502 }
 503 
 504 // System dictionary functions
 505 
 506 // find InstanceKlass by name
 507 function findInstanceKlass(name) {
 508    return sapkg.utilities.SystemDictionaryHelper.findInstanceKlass(name);
 509 }
 510 
 511 // get Java system loader (i.e., application launcher loader)
 512 function systemLoader() {
 513    return sa.sysDict.javaSystemLoader();
 514 }
 515 
 516 // iterate system dictionary for each 'Klass' 
 517 function forEachKlass(callback) {
 518    var VisitorClass = sapkg.memory.SystemDictionary.ClassVisitor;
 519    var visitor = new VisitorClass() { visit: callback };
 520    sa.sysDict["classesDo(sun.jvm.hotspot.memory.SystemDictionary.ClassVisitor)"](visitor);
 521 }
 522 
 523 // iterate system dictionary for each 'Klass' and initiating loader
 524 function forEachKlassAndLoader(callback) {
 525    var VisitorClass = sapkg.memory.SystemDictionary.ClassAndLoaderVisitor;
 526    var visitor = new VisitorClass() { visit: callback };
 527    sa.sysDict["classesDo(sun.jvm.hotspot.memory.SystemDictionary.ClassAndLoaderVisitor)"](visitor);
 528 }
 529 
 530 // iterate system dictionary for each primitive array klass
 531 function forEachPrimArrayKlass(callback) {
 532    var VisitorClass = sapkg.memory.SystemDictionary.ClassAndLoaderVisitor;
 533    sa.sysDict.primArrayClassesDo(new VisitorClass() { visit: callback });
 534 }
 535 
 536 // 'oop' to higher-level java object wrapper in which for(i in o) 
 537 // works by iterating java level fields and javaobject.javafield
 538 // syntax works.
 539 function oop2obj(oop) {
 540    return object(addr2str(oop.handle));
 541 }
 542 
 543 // higher level java object wrapper to oop
 544 function obj2oop(obj) {
 545    return addr2oop(str2addr(address(obj)));
 546 }
 547 
 548 // Java heap iteration
 549 
 550 // iterates Java heap for each Oop
 551 function forEachOop(callback) {
 552    function empty() { }
 553    sa.objHeap.iterate(new sapkg.oops.HeapVisitor() {
 554        prologue: empty,
 555        doObj: callback,
 556        epilogue: empty
 557    });
 558 }
 559 
 560 // iterates Java heap for each Oop of given 'klass'.
 561 // 'includeSubtypes' tells whether to include objects 
 562 // of subtypes of 'klass' or not
 563 function forEachOopOfKlass(callback, klass, includeSubtypes) {
 564    if (klass == undefined) {
 565        klass = findInstanceKlass("java.lang.Object");
 566    }
 567 
 568    if (includeSubtypes == undefined) {
 569       includeSubtypes = true;
 570    }
 571 
 572    function empty() { }
 573    sa.objHeap.iterateObjectsOfKlass(
 574         new sapkg.oops.HeapVisitor() {
 575             prologue: empty,
 576             doObj: callback,
 577             epilogue: empty
 578         },
 579         klass, includeSubtypes);
 580 }
 581 
 582 // Java thread
 583 
 584 // iterates each Thread
 585 function forEachJavaThread(callback) {
 586    var threads = sa.threads;
 587    var thread = threads.first();
 588    while (thread != null) {
 589       if (callback(thread) == false) return;
 590       thread = thread.next();
 591    }  
 592 }
 593 
 594 // iterate Frames of a given thread
 595 function forEachFrame(javaThread, callback) {
 596    var fr = javaThread.getLastFrameDbg();
 597    while (fr != null) { 
 598      if (callback(fr) == false) return;
 599      fr = fr.sender();
 600    }
 601 }
 602 
 603 // iterate JavaVFrames of a given JavaThread
 604 function forEachVFrame(javaThread, callback) {
 605    var vfr = javaThread.getLastJavaVFrameDbg();
 606    while (vfr != null) {
 607       if (callback(vfr) == false) return;
 608       vfr = vfr.javaSender();
 609    }
 610 }
 611 
 612 function printStackTrace(javaThread) {
 613    write("Thread ");
 614    javaThread.printThreadIDOn(java.lang.System.out);
 615    writeln();
 616    forEachVFrame(javaThread, function (vf) {
 617       var method = vf.method;
 618       write(' - ', method.externalNameAndSignature(), '@bci =', vf.getBCI());
 619       var line = method.getLineNumberFromBCI(vf.getBCI());
 620       if (line != -1) { write(', line=', line); }
 621       if (vf.isCompiledFrame()) { write(" (Compiled Frame)"); }
 622       if (vf.isInterpretedFrame()) { write(" (Interpreted Frame)"); }
 623       writeln();
 624    });
 625    writeln();
 626    writeln();
 627 }
 628 
 629 // print Java stack trace for all threads
 630 function where(javaThread) {
 631    if (javaThread == undefined) {
 632       forEachJavaThread(function (jt) { printStackTrace(jt); });
 633    } else {
 634       printStackTrace(javaThread);
 635    }
 636 }
 637 
 638 // vmStructs access -- type database functions
 639 
 640 // find a VM type
 641 function findVMType(typeName) {
 642    return sa.typedb.lookupType(typeName);
 643 }
 644 
 645 // iterate VM types
 646 function forEachVMType(callback) {
 647    var itr = sa.typedb.types;
 648    while (itr.hasNext()) {
 649       if (callback(itr.next()) == false) return;
 650    }
 651 }
 652 
 653 // find VM int constant
 654 function findVMIntConst(name) {
 655    return sa.typedb.lookupIntConstant(name);
 656 }
 657 
 658 // find VM long constant
 659 function findVMLongConst(name) {
 660    return sa.typedb.lookupLongConstant(name);
 661 }
 662 
 663 // iterate VM int constants
 664 function forEachVMIntConst(callback) {
 665    var itr = sa.typedb.intConstants;
 666    while (itr.hasNext()) {
 667       if (callback(itr.next()) == false) return;
 668    } 
 669 }
 670 
 671 // iterate VM long constants
 672 function forEachVMLongConst(callback) {
 673    var itr = sa.typedb.longConstants;
 674    while (itr.hasNext()) {
 675       if (callback(itr.next()) == false) return;
 676    } 
 677 }
 678 
 679 // returns VM Type at address
 680 function vmTypeof(addr) {
 681    addr = any2addr(addr);
 682    return sa.typedb.guessTypeForAddress(addr);
 683 }
 684 
 685 // does the given 'addr' points to an object of given 'type'?
 686 // OR any valid Type at all (if type is undefined)
 687 function isOfVMType(addr, type) {
 688    addr = any2addr(addr);
 689    if (type == undefined) {
 690       return vmTypeof(addr) != null;
 691    } else {
 692       if (typeof(type) == 'string') {
 693          type = findVMType(type);
 694       } 
 695       return sa.typedb.addressTypeIsEqualToType(addr, type);
 696    }
 697 }
 698 
 699 // reads static field value
 700 function readVMStaticField(field) {
 701    var type = field.type;
 702    if (type.isCIntegerType() || type.isJavaPrimitiveType()) {
 703       return field.value;
 704    } else if (type.isPointerType()) {
 705       return field.address;
 706    } else if (type.isOopType()) {
 707       return field.oopHandle;      
 708    } else {
 709       return field.staticFieldAddress;
 710    }
 711 }
 712 
 713 // reads given instance field of VM object at 'addr'
 714 function readVMInstanceField(field, addr) {
 715    var type = field.type;
 716    if (type.isCIntegerType() || type.isJavaPrimitiveType()) {
 717       return field.getValue(addr);
 718    } else if (type.isPointerType()) {
 719       return field.getAddress(addr);
 720    } else if (type.isOopType()) {
 721       return field.getOopHandle(addr);
 722    } else {
 723       return addr.addOffsetTo(field.offset);
 724    }
 725 }
 726 
 727 // returns name-value of pairs of VM type at given address.
 728 // If address is unspecified, reads static fields as name-value pairs.
 729 function readVMType(type, addr) {
 730    if (typeof(type) == 'string') {
 731       type = findVMType(type);
 732    }
 733    if (addr != undefined) {
 734       addr = any2addr(addr);
 735    }
 736 
 737    var result = new Object();
 738    var staticOnly = (addr == undefined);
 739    while (type != null) {
 740       var itr = type.fields;
 741       while (itr.hasNext()) {
 742          var field = itr.next();
 743          var isStatic = field.isStatic();
 744          if (staticOnly && isStatic) {
 745             result[field.name] = readVMStaticField(field);
 746          } else if (!staticOnly && !isStatic) {
 747             result[field.name] = readVMInstanceField(field, addr);
 748          }
 749       }
 750       type = type.superclass;
 751    } 
 752    return result;
 753 }
 754 
 755 function printVMType(type, addr) {
 756    if (typeof(type) == 'string') {
 757       type = findVMType(type);
 758    }
 759    var obj = readVMType(type, addr);
 760    while (type != null) {
 761       var itr = type.fields;
 762       while (itr.hasNext()) {
 763          var field = itr.next();
 764          var name = field.name;
 765          var value = obj[name];
 766          if (value != undefined) {
 767             writeln(field.type.name, type.name + '::' + name, '=', value);
 768          }
 769       }
 770       type = type.superclass;  
 771    }
 772 }
 773 
 774 // define readXXX and printXXX functions for each VM struct/class Type
 775 tmp = new Object();
 776 tmp.itr = sa.typedb.types;
 777 while (tmp.itr.hasNext()) {
 778    tmp.type = tmp.itr.next();
 779    tmp.name = tmp.type.name;
 780    if (tmp.type.isPointerType() || tmp.type.isOopType() ||
 781       tmp.type.isCIntegerType() || tmp.type.isJavaPrimitiveType() ||
 782       tmp.name.equals('address') ||
 783       tmp.name.equals("<opaque>")) {
 784          // ignore;
 785          continue;
 786    } else {
 787       // some type names have ':', '<', '>', '*', ' '. replace to make it as a
 788       // JavaScript identifier
 789       tmp.name = ("" + tmp.name).replace(/[:<>* ]/g, '_');
 790       eval("function read" + tmp.name + "(addr) {" +
 791            "   return readVMType('" + tmp.name + "', addr);}"); 
 792       eval("function print" + tmp.name + "(addr) {" + 
 793            "   printVMType('" + tmp.name + "', addr); }");
 794 
 795       /* FIXME: do we need this?
 796       if (typeof(registerCommand) != 'undefined') {
 797           var name = "print" + tmp.name;
 798           registerCommand(name, name + " [address]", name);
 799       }
 800       */
 801    }
 802 }
 803 //clean-up the temporary
 804 delete tmp;
 805 
 806 // VMObject factory
 807 
 808 // VM type to SA class map
 809 var  vmType2Class = new Object();
 810 
 811 // C2 only classes
 812 try{
 813   vmType2Class["ExceptionBlob"] = sapkg.code.ExceptionBlob;
 814   vmType2Class["UncommonTrapBlob"] = sapkg.code.UncommonTrapBlob;
 815 } catch(e) {
 816   // Ignore exception. C2 specific objects might be not 
 817   // available in client VM
 818 }
 819 
 820 
 821 // This is *not* exhaustive. Add more if needed.
 822 // code blobs
 823 vmType2Class["BufferBlob"] = sapkg.code.BufferBlob;
 824 vmType2Class["nmethod"] = sapkg.code.NMethod;
 825 vmType2Class["RuntimeStub"] = sapkg.code.RuntimeStub;
 826 vmType2Class["SafepointBlob"] = sapkg.code.SafepointBlob;
 827 vmType2Class["C2IAdapter"] = sapkg.code.C2IAdapter;
 828 vmType2Class["DeoptimizationBlob"] = sapkg.code.DeoptimizationBlob;
 829 vmType2Class["I2CAdapter"] = sapkg.code.I2CAdapter;
 830 vmType2Class["OSRAdapter"] = sapkg.code.OSRAdapter;
 831 vmType2Class["PCDesc"] = sapkg.code.PCDesc;
 832 
 833 // interpreter
 834 vmType2Class["InterpreterCodelet"] = sapkg.interpreter.InterpreterCodelet;
 835 
 836 // Java Threads
 837 vmType2Class["JavaThread"] = sapkg.runtime.JavaThread;
 838 vmType2Class["CompilerThread"] = sapkg.runtime.CompilerThread;
 839 vmType2Class["CodeCacheSweeperThread"] = sapkg.runtime.CodeCacheSweeperThread;
 840 vmType2Class["DebuggerThread"] = sapkg.runtime.DebuggerThread;
 841 
 842 // gc
 843 vmType2Class["GenCollectedHeap"] = sapkg.memory.GenCollectedHeap;
 844 vmType2Class["DefNewGeneration"] = sapkg.memory.DefNewGeneration;
 845 vmType2Class["TenuredGeneration"] = sapkg.memory.TenuredGeneration;
 846 
 847 // generic VMObject factory for a given address
 848 // This is equivalent to VirtualConstructor.
 849 function newVMObject(addr) {
 850    addr = any2addr(addr);
 851    var result = null;
 852    forEachVMType(function (type) {
 853                     if (isOfVMType(addr, type)) {
 854                        var clazz = vmType2Class[type.name];
 855                        if (clazz != undefined) {
 856                           result = new clazz(addr);
 857                        }
 858                        return false;
 859                     } else {
 860                        return true;
 861                     }
 862                  });
 863    return result;
 864 }
 865 
 866 function vmobj2addr(vmobj) {
 867    return vmobj.address;
 868 }
 869 
 870 function addr2vmobj(addr) {
 871    return newVMObject(addr);
 872 }     
 873 
 874 // Miscellaneous utilities
 875 
 876 // returns PointerLocation that describes the given pointer
 877 function findPtr(addr) {
 878    addr = any2addr(addr);
 879    return sapkg.utilities.PointerFinder.find(addr);
 880 }
 881 
 882 // is given address a valid Oop?
 883 function isOop(addr) {
 884    addr = any2addr(addr);
 885    var oopHandle = addr.addOffsetToAsOopHandle(0);
 886    return sapkg.utilities.RobustOopDeterminator.oopLooksValid(oopHandle);
 887 }
 888 
 889 // returns description of given pointer as a String
 890 function whatis(addr) {
 891   addr = any2addr(addr);
 892   var ptrLoc = findPtr(addr);
 893   if (!ptrLoc.isUnknown()) {
 894     return ptrLoc.toString();
 895   }
 896 
 897   var vmType = vmTypeof(addr);
 898   if (vmType != null) {
 899     return "pointer to " + vmType.name;
 900   }
 901 
 902   var dso = loadObjectContainingPC(addr);
 903   if (dso == null) {
 904     return ptrLoc.toString();
 905   }
 906 
 907   var sym = dso.closestSymbolToPC(addr);
 908   if (sym != null) {
 909     return sym.name + '+' + sym.offset;
 910   }
 911 
 912   var s = dso.getName();
 913   var p = s.lastIndexOf("/");
 914   var base = dso.getBase();
 915   return s.substring(p+1, s.length) + '+' + addr.minus(base);
 916 }