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