1 /*
   2  * Copyright (c) 2004, 2017, 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 // returns the ClosestSymbol or null
 375 function closestSymbolFor(addr) {
 376    if (sa.cdbg == null) {
 377       // no CDebugger support, return null
 378       return null;
 379    } else {
 380       var dso = sa.cdbg.loadObjectContainingPC(addr);
 381       if (dso != null) {
 382          return dso.closestSymbolToPC(addr);
 383       } else {
 384          return null;
 385       }
 386    }
 387 }
 388 
 389 // Address-to-symbol
 390 // returns nearest symbol as string if found
 391 // else returns address as string
 392 function addr2sym(addr) {
 393     var sym = closestSymbolFor(addr);
 394     if (sym != null)  {
 395        return sym.name + '+' + sym.offset;
 396     } else {
 397        return addr2str(addr);
 398     }
 399 }
 400 
 401 // read 'num' words at 'addr' and return an array as result.
 402 // returns Java long[] type result and not a JavaScript array.
 403 function readWordsAt(addr, num) {
 404    addr = any2addr(addr);
 405    var res = java.lang.reflect.Array.newInstance(java.lang.Long.TYPE, num);
 406    var i;
 407    for (i = 0; i < num; i++) {
 408       res[i] = addr2num(addr.getAddressAt(i * addressSize));
 409    }
 410    return res;
 411 }
 412 
 413 // read the 'C' string at 'addr'
 414 function readCStrAt(addr) {
 415    addr = any2addr(addr);
 416    return sapkg.utilities.CStringUtilities.getString(addr);
 417 }
 418 
 419 // read the length of the 'C' string at 'addr'
 420 function readCStrLen(addr) {
 421    addr = any2addr(addr);
 422    return sapkg.utilities.CStringUtilities.getStringLength(addr);
 423 }
 424 
 425 // iterate through ThreadList of CDebugger
 426 function forEachThread(callback) {
 427    if (sa.cdbg == null) {
 428       // no CDebugger support
 429       return;
 430    } else {
 431       var itr = sa.cdbg.threadList.iterator();
 432       while (itr.hasNext()) {
 433          if (callback(itr.next()) == false) return;
 434       }
 435    }
 436 }
 437 
 438 // read register set of a ThreadProxy as name-value pairs
 439 function readRegs(threadProxy) {
 440    var ctx = threadProxy.context;
 441    var num = ctx.numRegisters;
 442    var res = new Object();
 443    var i;
 444    for (i = 0; i < num; i++) {
 445       res[ctx.getRegisterName(i)]= addr2str(ctx.getRegisterAsAddress(i));
 446    }
 447    return res;
 448 }
 449 
 450 // print register set for a given ThreaProxy
 451 function regs(threadProxy) {
 452    var res = readRegs(threadProxy);
 453    for (i in res) {
 454       writeln(i, '=', res[i]);
 455    }
 456 }
 457 
 458 // iterate through each CFrame of a given ThreadProxy
 459 function forEachCFrame(threadProxy, callback) {   
 460    if (sa.cdbg == null) {
 461       // no CDebugger support
 462       return;
 463    } else {
 464       var cframe = sa.cdbg.topFrameForThread(threadProxy);
 465       while (cframe != null) {
 466          if (callback(cframe) == false) return;
 467          cframe = cframe.sender();
 468       }
 469    }
 470 }
 471 
 472 // iterate through list of load objects (DLLs, DSOs)
 473 function forEachLoadObject(callback) {
 474    if (sa.cdbg == null) {
 475       // no CDebugger support
 476       return;
 477    } else {
 478       var itr = sa.cdbg.loadObjectList.iterator();
 479       while (itr.hasNext()) {
 480          if (callback(itr.next()) == false) return;
 481       }
 482    }
 483 }
 484 
 485 // print 'num' words at 'addr'
 486 function mem(addr, num) {
 487    if (num == undefined) {
 488       num = 1;
 489    }
 490    addr = any2addr(addr);   
 491    var i;
 492    for (i = 0; i < num; i++) {
 493       var value = addr.getAddressAt(0);      
 494       writeln(addr2sym(addr) + ':', addr2str(value)); 
 495       addr = addr.addOffsetTo(addressSize);      
 496    }
 497    writeln();
 498 }
 499 
 500 // System dictionary functions
 501 
 502 // find InstanceKlass by name
 503 function findInstanceKlass(name) {
 504    return sapkg.utilities.SystemDictionaryHelper.findInstanceKlass(name);
 505 }
 506 
 507 // get Java system loader (i.e., application launcher loader)
 508 function systemLoader() {
 509    return sa.sysDict.javaSystemLoader();
 510 }
 511 
 512 // iterate system dictionary for each 'Klass' 
 513 function forEachKlass(callback) {
 514    var VisitorClass = sapkg.memory.SystemDictionary.ClassVisitor;
 515    var visitor = new VisitorClass() { visit: callback };
 516    sa.sysDict["classesDo(sun.jvm.hotspot.memory.SystemDictionary.ClassVisitor)"](visitor);
 517 }
 518 
 519 // iterate system dictionary for each 'Klass' and initiating loader
 520 function forEachKlassAndLoader(callback) {
 521    var VisitorClass = sapkg.memory.SystemDictionary.ClassAndLoaderVisitor;
 522    var visitor = new VisitorClass() { visit: callback };
 523    sa.sysDict["classesDo(sun.jvm.hotspot.memory.SystemDictionary.ClassAndLoaderVisitor)"](visitor);
 524 }
 525 
 526 // iterate system dictionary for each primitive array klass
 527 function forEachPrimArrayKlass(callback) {
 528    var VisitorClass = sapkg.memory.SystemDictionary.ClassAndLoaderVisitor;
 529    sa.sysDict.primArrayClassesDo(new VisitorClass() { visit: callback });
 530 }
 531 
 532 // 'oop' to higher-level java object wrapper in which for(i in o) 
 533 // works by iterating java level fields and javaobject.javafield
 534 // syntax works.
 535 function oop2obj(oop) {
 536    return object(addr2str(oop.handle));
 537 }
 538 
 539 // higher level java object wrapper to oop
 540 function obj2oop(obj) {
 541    return addr2oop(str2addr(address(obj)));
 542 }
 543 
 544 // Java heap iteration
 545 
 546 // iterates Java heap for each Oop
 547 function forEachOop(callback) {
 548    function empty() { }
 549    sa.objHeap.iterate(new sapkg.oops.HeapVisitor() {
 550        prologue: empty,
 551        doObj: callback,
 552        epilogue: empty
 553    });
 554 }
 555 
 556 // iterates Java heap for each Oop of given 'klass'.
 557 // 'includeSubtypes' tells whether to include objects 
 558 // of subtypes of 'klass' or not
 559 function forEachOopOfKlass(callback, klass, includeSubtypes) {
 560    if (klass == undefined) {
 561        klass = findInstanceKlass("java.lang.Object");
 562    }
 563 
 564    if (includeSubtypes == undefined) {
 565       includeSubtypes = true;
 566    }
 567 
 568    function empty() { }
 569    sa.objHeap.iterateObjectsOfKlass(
 570         new sapkg.oops.HeapVisitor() {
 571             prologue: empty,
 572             doObj: callback,
 573             epilogue: empty
 574         },
 575         klass, includeSubtypes);
 576 }
 577 
 578 // Java thread
 579 
 580 // iterates each Thread
 581 function forEachJavaThread(callback) {
 582    var threads = sa.threads;
 583    var thread = threads.first();
 584    while (thread != null) {
 585       if (callback(thread) == false) return;
 586       thread = thread.next();
 587    }  
 588 }
 589 
 590 // iterate Frames of a given thread
 591 function forEachFrame(javaThread, callback) {
 592    var fr = javaThread.getLastFrameDbg();
 593    while (fr != null) { 
 594      if (callback(fr) == false) return;
 595      fr = fr.sender();
 596    }
 597 }
 598 
 599 // iterate JavaVFrames of a given JavaThread
 600 function forEachVFrame(javaThread, callback) {
 601    var vfr = javaThread.getLastJavaVFrameDbg();
 602    while (vfr != null) {
 603       if (callback(vfr) == false) return;
 604       vfr = vfr.javaSender();
 605    }
 606 }
 607 
 608 function printStackTrace(javaThread) {
 609    write("Thread ");
 610    javaThread.printThreadIDOn(java.lang.System.out);
 611    writeln();
 612    forEachVFrame(javaThread, function (vf) {
 613       var method = vf.method;
 614       write(' - ', method.externalNameAndSignature(), '@bci =', vf.getBCI());
 615       var line = method.getLineNumberFromBCI(vf.getBCI());
 616       if (line != -1) { write(', line=', line); }
 617       if (vf.isCompiledFrame()) { write(" (Compiled Frame)"); }
 618       if (vf.isInterpretedFrame()) { write(" (Interpreted Frame)"); }
 619       writeln();
 620    });
 621    writeln();
 622    writeln();
 623 }
 624 
 625 // print Java stack trace for all threads
 626 function where(javaThread) {
 627    if (javaThread == undefined) {
 628       forEachJavaThread(function (jt) { printStackTrace(jt); });
 629    } else {
 630       printStackTrace(javaThread);
 631    }
 632 }
 633 
 634 // vmStructs access -- type database functions
 635 
 636 // find a VM type
 637 function findVMType(typeName) {
 638    return sa.typedb.lookupType(typeName);
 639 }
 640 
 641 // iterate VM types
 642 function forEachVMType(callback) {
 643    var itr = sa.typedb.types;
 644    while (itr.hasNext()) {
 645       if (callback(itr.next()) == false) return;
 646    }
 647 }
 648 
 649 // find VM int constant
 650 function findVMIntConst(name) {
 651    return sa.typedb.lookupIntConstant(name);
 652 }
 653 
 654 // find VM long constant
 655 function findVMLongConst(name) {
 656    return sa.typedb.lookupLongConstant(name);
 657 }
 658 
 659 // iterate VM int constants
 660 function forEachVMIntConst(callback) {
 661    var itr = sa.typedb.intConstants;
 662    while (itr.hasNext()) {
 663       if (callback(itr.next()) == false) return;
 664    } 
 665 }
 666 
 667 // iterate VM long constants
 668 function forEachVMLongConst(callback) {
 669    var itr = sa.typedb.longConstants;
 670    while (itr.hasNext()) {
 671       if (callback(itr.next()) == false) return;
 672    } 
 673 }
 674 
 675 // returns VM Type at address
 676 function vmTypeof(addr) {
 677    addr = any2addr(addr);
 678    return sa.typedb.guessTypeForAddress(addr);
 679 }
 680 
 681 // does the given 'addr' points to an object of given 'type'?
 682 // OR any valid Type at all (if type is undefined)
 683 function isOfVMType(addr, type) {
 684    addr = any2addr(addr);
 685    if (type == undefined) {
 686       return vmTypeof(addr) != null;
 687    } else {
 688       if (typeof(type) == 'string') {
 689          type = findVMType(type);
 690       } 
 691       return sa.typedb.addressTypeIsEqualToType(addr, type);
 692    }
 693 }
 694 
 695 // reads static field value
 696 function readVMStaticField(field) {
 697    var type = field.type;
 698    if (type.isCIntegerType() || type.isJavaPrimitiveType()) {
 699       return field.value;
 700    } else if (type.isPointerType()) {
 701       return field.address;
 702    } else if (type.isOopType()) {
 703       return field.oopHandle;      
 704    } else {
 705       return field.staticFieldAddress;
 706    }
 707 }
 708 
 709 // reads given instance field of VM object at 'addr'
 710 function readVMInstanceField(field, addr) {
 711    var type = field.type;
 712    if (type.isCIntegerType() || type.isJavaPrimitiveType()) {
 713       return field.getValue(addr);
 714    } else if (type.isPointerType()) {
 715       return field.getAddress(addr);
 716    } else if (type.isOopType()) {
 717       return field.getOopHandle(addr);
 718    } else {
 719       return addr.addOffsetTo(field.offset);
 720    }
 721 }
 722 
 723 // returns name-value of pairs of VM type at given address.
 724 // If address is unspecified, reads static fields as name-value pairs.
 725 function readVMType(type, addr) {
 726    if (typeof(type) == 'string') {
 727       type = findVMType(type);
 728    }
 729    if (addr != undefined) {
 730       addr = any2addr(addr);
 731    }
 732 
 733    var result = new Object();
 734    var staticOnly = (addr == undefined);
 735    while (type != null) {
 736       var itr = type.fields;
 737       while (itr.hasNext()) {
 738          var field = itr.next();
 739          var isStatic = field.isStatic();
 740          if (staticOnly && isStatic) {
 741             result[field.name] = readVMStaticField(field);
 742          } else if (!staticOnly && !isStatic) {
 743             result[field.name] = readVMInstanceField(field, addr);
 744          }
 745       }
 746       type = type.superclass;
 747    } 
 748    return result;
 749 }
 750 
 751 function printVMType(type, addr) {
 752    if (typeof(type) == 'string') {
 753       type = findVMType(type);
 754    }
 755    var obj = readVMType(type, addr);
 756    while (type != null) {
 757       var itr = type.fields;
 758       while (itr.hasNext()) {
 759          var field = itr.next();
 760          var name = field.name;
 761          var value = obj[name];
 762          if (value != undefined) {
 763             writeln(field.type.name, type.name + '::' + name, '=', value);
 764          }
 765       }
 766       type = type.superclass;  
 767    }
 768 }
 769 
 770 // define readXXX and printXXX functions for each VM struct/class Type
 771 tmp = new Object();
 772 tmp.itr = sa.typedb.types;
 773 while (tmp.itr.hasNext()) {
 774    tmp.type = tmp.itr.next();
 775    tmp.name = tmp.type.name;
 776    if (tmp.type.isPointerType() || tmp.type.isOopType() ||
 777       tmp.type.isCIntegerType() || tmp.type.isJavaPrimitiveType() ||
 778       tmp.name.equals('address') ||
 779       tmp.name.equals("<opaque>")) {
 780          // ignore;
 781          continue;
 782    } else {
 783       // some type names have ':', '<', '>', '*', ' '. replace to make it as a
 784       // JavaScript identifier
 785       tmp.name = ("" + tmp.name).replace(/[:<>* ]/g, '_');
 786       eval("function read" + tmp.name + "(addr) {" +
 787            "   return readVMType('" + tmp.name + "', addr);}"); 
 788       eval("function print" + tmp.name + "(addr) {" + 
 789            "   printVMType('" + tmp.name + "', addr); }");
 790 
 791       /* FIXME: do we need this?
 792       if (typeof(registerCommand) != 'undefined') {
 793           var name = "print" + tmp.name;
 794           registerCommand(name, name + " [address]", name);
 795       }
 796       */
 797    }
 798 }
 799 //clean-up the temporary
 800 delete tmp;
 801 
 802 // VMObject factory
 803 
 804 // VM type to SA class map
 805 var  vmType2Class = new Object();
 806 
 807 // C2 only classes
 808 try{
 809   vmType2Class["ExceptionBlob"] = sapkg.code.ExceptionBlob;
 810   vmType2Class["UncommonTrapBlob"] = sapkg.code.UncommonTrapBlob;
 811 } catch(e) {
 812   // Ignore exception. C2 specific objects might be not 
 813   // available in client VM
 814 }
 815 
 816 
 817 // This is *not* exhaustive. Add more if needed.
 818 // code blobs
 819 vmType2Class["BufferBlob"] = sapkg.code.BufferBlob;
 820 vmType2Class["nmethod"] = sapkg.code.NMethod;
 821 vmType2Class["RuntimeStub"] = sapkg.code.RuntimeStub;
 822 vmType2Class["SafepointBlob"] = sapkg.code.SafepointBlob;
 823 vmType2Class["C2IAdapter"] = sapkg.code.C2IAdapter;
 824 vmType2Class["DeoptimizationBlob"] = sapkg.code.DeoptimizationBlob;
 825 vmType2Class["I2CAdapter"] = sapkg.code.I2CAdapter;
 826 vmType2Class["OSRAdapter"] = sapkg.code.OSRAdapter;
 827 vmType2Class["PCDesc"] = sapkg.code.PCDesc;
 828 
 829 // interpreter
 830 vmType2Class["InterpreterCodelet"] = sapkg.interpreter.InterpreterCodelet;
 831 
 832 // Java Threads
 833 vmType2Class["JavaThread"] = sapkg.runtime.JavaThread;
 834 vmType2Class["CompilerThread"] = sapkg.runtime.CompilerThread;
 835 vmType2Class["SurrogateLockerThread"] = sapkg.runtime.JavaThread;
 836 vmType2Class["DebuggerThread"] = sapkg.runtime.DebuggerThread;
 837 
 838 // gc
 839 vmType2Class["GenCollectedHeap"] = sapkg.memory.GenCollectedHeap;
 840 vmType2Class["DefNewGeneration"] = sapkg.memory.DefNewGeneration;
 841 vmType2Class["TenuredGeneration"] = sapkg.memory.TenuredGeneration;
 842 
 843 // generic VMObject factory for a given address
 844 // This is equivalent to VirtualConstructor.
 845 function newVMObject(addr) {
 846    addr = any2addr(addr);
 847    var result = null;
 848    forEachVMType(function (type) {
 849                     if (isOfVMType(addr, type)) {
 850                        var clazz = vmType2Class[type.name];
 851                        if (clazz != undefined) {
 852                           result = new clazz(addr);
 853                        }
 854                        return false;
 855                     } else {
 856                        return true;
 857                     }
 858                  });
 859    return result;
 860 }
 861 
 862 function vmobj2addr(vmobj) {
 863    return vmobj.address;
 864 }
 865 
 866 function addr2vmobj(addr) {
 867    return newVMObject(addr);
 868 }     
 869 
 870 // Miscellaneous utilities
 871 
 872 // returns PointerLocation that describes the given pointer
 873 function findPtr(addr) {
 874    addr = any2addr(addr);
 875    return sapkg.utilities.PointerFinder.find(addr);
 876 }
 877 
 878 // is given address a valid Oop?
 879 function isOop(addr) {
 880    addr = any2addr(addr);
 881    var oopHandle = addr.addOffsetToAsOopHandle(0);
 882    return sapkg.utilities.RobustOopDeterminator.oopLooksValid(oopHandle);
 883 }
 884 
 885 // returns description of given pointer as a String
 886 function whatis(addr) {
 887    addr = any2addr(addr);
 888    var ptrLoc = findPtr(addr);
 889    if (ptrLoc.isUnknown()) {
 890       var vmType = vmTypeof(addr);
 891       if (vmType != null) {
 892          return "pointer to " + vmType.name;
 893       } else {
 894          var sym = closestSymbolFor(addr);
 895          if (sym != null) {
 896             return sym.name + '+' + sym.offset;
 897          } else {
 898             return ptrLoc.toString();
 899          }
 900       }
 901    } else {
 902       return ptrLoc.toString();
 903    }
 904 }