blob: a700f1bc6bdcfa31e7506fa91db5ff1a213f7495 [file] [log] [blame]
/*
* Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
// shorter names for SA packages
// SA package name abbreviations are kept in 'sapkg' object
// to avoid global namespace pollution
var sapkg = new Object();
sapkg.hotspot = Packages.sun.jvm.hotspot;
sapkg.asm = sapkg.hotspot.asm;
sapkg.c1 = sapkg.hotspot.c1;
sapkg.code = sapkg.hotspot.code;
sapkg.compiler = sapkg.hotspot.compiler;
// 'debugger' is a JavaScript keyword, but ES5 relaxes the
// restriction of using keywords as property name
sapkg.debugger = sapkg.hotspot.debugger;
sapkg.interpreter = sapkg.hotspot.interpreter;
sapkg.jdi = sapkg.hotspot.jdi;
sapkg.memory = sapkg.hotspot.memory;
sapkg.oops = sapkg.hotspot.oops;
sapkg.runtime = sapkg.hotspot.runtime;
sapkg.tools = sapkg.hotspot.tools;
sapkg.types = sapkg.hotspot.types;
sapkg.ui = sapkg.hotspot.ui;
sapkg.utilities = sapkg.hotspot.utilities;
// SA singletons are kept in 'sa' object
var sa = new Object();
sa.vm = sapkg.runtime.VM.getVM();
sa.dbg = sa.vm.getDebugger();
sa.cdbg = sa.dbg.CDebugger;
sa.heap = sa.vm.universe.heap();
sa.systemDictionary = sa.vm.systemDictionary;
sa.sysDict = sa.systemDictionary;
sa.symbolTable = sa.vm.symbolTable;
sa.symTbl = sa.symbolTable;
sa.threads = sa.vm.threads;
sa.interpreter = sa.vm.interpreter;
sa.typedb = sa.vm.typeDataBase;
sa.codeCache = sa.vm.codeCache;
// 'objHeap' is different from 'heap'!.
// This is SA's Oop factory and heap-walker
sa.objHeap = sa.vm.objectHeap;
// few useful global variables
var OS = sa.vm.OS;
var CPU = sa.vm.CPU;
var LP64 = sa.vm.LP64;
var isClient = sa.vm.clientCompiler;
var isServer = sa.vm.serverCompiler;
var isCore = sa.vm.isCore();
var addressSize = sa.vm.addressSize;
var oopSize = sa.vm.oopSize;
// this "main" function is called immediately
// after loading this script file
function main(globals, jvmarg) {
// wrap a sun.jvm.hotspot.utilities.soql.ScriptObject
// object so that the properties of it can be accessed
// in natural object.field syntax.
function wrapScriptObject(so) {
function unwrapScriptObject(wso) {
var objType = typeof(wso);
if ((objType == 'object' ||
objType == 'function')
&& "__wrapped__" in wso) {
return wso.__wrapped__;
} else {
return wso;
}
}
function prepareArgsArray(array) {
var args = new Array(array.length);
for (var a = 0; a < array.length; a++) {
var elem = array[a];
elem = unwrapScriptObject(elem);
if (typeof(elem) == 'function') {
args[a] = new sapkg.utilities.soql.Callable() {
call: function(myargs) {
var tmp = new Array(myargs.length);
for (var i = 0; i < myargs.length; i++) {
tmp[i] = wrapScriptObject(myargs[i]);
}
return elem.apply(this, tmp);
}
}
} else {
args[a] = elem;
}
}
return args;
}
// Handle __has__ specially to avoid metacircularity problems
// when called from __get__.
// Calling
// this.__has__(name)
// will in turn call
// this.__call__('__has__', name)
// which is not handled below
function __has__(name) {
if (typeof(name) == 'number') {
return so["has(int)"](name);
} else {
if (name == '__wrapped__') {
return true;
} else if (so["has(java.lang.String)"](name)) {
return true;
} else if (name.equals('toString')) {
return true;
} else {
return false;
}
}
}
if (so instanceof sapkg.utilities.soql.ScriptObject) {
return new JSAdapter() {
__getIds__: function() {
return so.getIds();
},
__has__ : __has__,
__delete__ : function(name) {
if (typeof(name) == 'number') {
return so["delete(int)"](name);
} else {
return so["delete(java.lang.String)"](name);
}
},
__get__ : function(name) {
// don't call this.__has__(name); see comments above function __has__
if (! __has__.call(this, name)) {
return undefined;
}
if (typeof(name) == 'number') {
return wrapScriptObject(so["get(int)"](name));
} else {
if (name == '__wrapped__') {
return so;
} else {
var value = so["get(java.lang.String)"](name);
if (value instanceof sapkg.utilities.soql.Callable) {
return function() {
var args = prepareArgsArray(arguments);
var r;
try {
r = value.call(Java.to(args, 'java.lang.Object[]'));
} catch (e) {
println("call to " + name + " failed!");
throw e;
}
return wrapScriptObject(r);
}
} else if (name == 'toString') {
return function() {
return so.toString();
}
} else {
return wrapScriptObject(value);
}
}
}
}
};
} else {
return so;
}
}
// set "jvm" global variable that wraps a
// sun.jvm.hotspot.utilities.soql.JSJavaVM instance
if (jvmarg != null) {
jvm = wrapScriptObject(jvmarg);
// expose "heap" global variable
heap = jvm.heap;
}
// expose all "function" type properties of
// sun.jvm.hotspot.utilitites.soql.JSJavaScriptEngine
// as global functions here.
globals = wrapScriptObject(globals);
for (var prop in globals) {
if (typeof(globals[prop]) == 'function') {
this[prop] = globals[prop];
}
}
// define "writeln" and "write" if not defined
if (typeof(println) == 'undefined') {
println = function (str) {
java.lang.System.out.println(String(str));
}
}
if (typeof(print) == 'undefined') {
print = function (str) {
java.lang.System.out.print(String(str));
}
}
if (typeof(writeln) == 'undefined') {
writeln = println;
}
if (typeof(write) == 'undefined') {
write = print;
}
// "registerCommand" function is defined if we
// are running as part of "CLHSDB" tool. CLHSDB
// tool exposes Unix-style commands.
// if "registerCommand" function is defined
// then register few global functions as "commands".
if (typeof(registerCommand) == 'function') {
this.jclass = function(name) {
if (typeof(name) == "string") {
var clazz = sapkg.utilities.SystemDictionaryHelper.findInstanceKlass(name);
if (clazz) {
writeln(clazz.getName().asString() + " @" + clazz.getAddress().toString());
} else {
writeln("class not found: " + name);
}
} else {
writeln("Usage: class name");
}
}
registerCommand("class", "class name", "jclass");
this.jclasses = function() {
forEachKlass(function (clazz) {
writeln(clazz.getName().asString() + " @" + clazz.getAddress().toString());
});
}
registerCommand("classes", "classes", "jclasses");
this.dclass = function(clazz, dir) {
if (!clazz) {
writeln("Usage: dumpclass { address | name } [ directory ]");
} else {
if (!dir) { dir = "."; }
dumpClass(clazz, dir);
}
}
registerCommand("dumpclass", "dumpclass { address | name } [ directory ]", "dclass");
registerCommand("dumpheap", "dumpheap [ file ]", "dumpHeap");
this.jseval = function(str) {
if (!str) {
writeln("Usage: jseval script");
} else {
var res = eval(str);
if (res) { writeln(res); }
}
}
registerCommand("jseval", "jseval script", "jseval");
this.jsload = function(file) {
if (!file) {
writeln("Usage: jsload file");
} else {
load(file);
}
}
registerCommand("jsload", "jsload file", "jsload");
this.printMem = function(addr, len) {
if (!addr) {
writeln("Usage: mem [ length ]");
} else {
mem(addr, len);
}
}
registerCommand("mem", "mem address [ length ]", "printMem");
this.sysProps = function() {
for (var i in jvm.sysProps) {
writeln(i + ' = ' + jvm.sysProps[i]);
}
}
registerCommand("sysprops", "sysprops", "sysProps");
this.printWhatis = function(addr) {
if (!addr) {
writeln("Usage: whatis address");
} else {
writeln(whatis(addr));
}
}
registerCommand("whatis", "whatis address", "printWhatis");
}
}
// debugger functionality
// string-to-Address
function str2addr(str) {
return sa.dbg.parseAddress(str);
}
// number-to-Address
if (addressSize == 4) {
eval("function num2addr(num) { \
return str2addr('0x' + java.lang.Integer.toHexString(0xffffffff & num)); \
}");
} else {
eval("function num2addr(num) { \
return str2addr('0x' + java.lang.Long.toHexString(num)); \
}");
}
// generic any-type-to-Address
// use this convenience function to accept address in any
// format -- number, string or an Address instance.
function any2addr(addr) {
var type = typeof(addr);
if (type == 'number') {
return num2addr(addr);
} else if (type == 'string') {
return str2addr(addr);
} else {
return addr;
}
}
// Address-to-string
function addr2str(addr) {
if (addr == null) {
return (addressSize == 4)? '0x00000000' : '0x0000000000000000';
} else {
return addr + '';
}
}
// Address-to-number
function addr2num(addr) {
return sa.dbg.getAddressValue(addr);
}
// symbol-to-Address
function sym2addr(dso, sym) {
return sa.dbg.lookup(dso, sym);
}
// returns the ClosestSymbol or null
function closestSymbolFor(addr) {
if (sa.cdbg == null) {
// no CDebugger support, return null
return null;
} else {
var dso = sa.cdbg.loadObjectContainingPC(addr);
if (dso != null) {
return dso.closestSymbolToPC(addr);
} else {
return null;
}
}
}
// Address-to-symbol
// returns nearest symbol as string if found
// else returns address as string
function addr2sym(addr) {
var sym = closestSymbolFor(addr);
if (sym != null) {
return sym.name + '+' + sym.offset;
} else {
return addr2str(addr);
}
}
// read 'num' words at 'addr' and return an array as result.
// returns Java long[] type result and not a JavaScript array.
function readWordsAt(addr, num) {
addr = any2addr(addr);
var res = java.lang.reflect.Array.newInstance(java.lang.Long.TYPE, num);
var i;
for (i = 0; i < num; i++) {
res[i] = addr2num(addr.getAddressAt(i * addressSize));
}
return res;
}
// read the 'C' string at 'addr'
function readCStrAt(addr) {
addr = any2addr(addr);
return sapkg.utilities.CStringUtilities.getString(addr);
}
// read the length of the 'C' string at 'addr'
function readCStrLen(addr) {
addr = any2addr(addr);
return sapkg.utilities.CStringUtilities.getStringLength(addr);
}
// iterate through ThreadList of CDebugger
function forEachThread(callback) {
if (sa.cdbg == null) {
// no CDebugger support
return;
} else {
var itr = sa.cdbg.threadList.iterator();
while (itr.hasNext()) {
if (callback(itr.next()) == false) return;
}
}
}
// read register set of a ThreadProxy as name-value pairs
function readRegs(threadProxy) {
var ctx = threadProxy.context;
var num = ctx.numRegisters;
var res = new Object();
var i;
for (i = 0; i < num; i++) {
res[ctx.getRegisterName(i)]= addr2str(ctx.getRegisterAsAddress(i));
}
return res;
}
// print register set for a given ThreaProxy
function regs(threadProxy) {
var res = readRegs(threadProxy);
for (i in res) {
writeln(i, '=', res[i]);
}
}
// iterate through each CFrame of a given ThreadProxy
function forEachCFrame(threadProxy, callback) {
if (sa.cdbg == null) {
// no CDebugger support
return;
} else {
var cframe = sa.cdbg.topFrameForThread(threadProxy);
while (cframe != null) {
if (callback(cframe) == false) return;
cframe = cframe.sender();
}
}
}
// iterate through list of load objects (DLLs, DSOs)
function forEachLoadObject(callback) {
if (sa.cdbg == null) {
// no CDebugger support
return;
} else {
var itr = sa.cdbg.loadObjectList.iterator();
while (itr.hasNext()) {
if (callback(itr.next()) == false) return;
}
}
}
// print 'num' words at 'addr'
function mem(addr, num) {
if (num == undefined) {
num = 1;
}
addr = any2addr(addr);
var i;
for (i = 0; i < num; i++) {
var value = addr.getAddressAt(0);
writeln(addr2sym(addr) + ':', addr2str(value));
addr = addr.addOffsetTo(addressSize);
}
writeln();
}
// System dictionary functions
// find InstanceKlass by name
function findInstanceKlass(name) {
return sapkg.utilities.SystemDictionaryHelper.findInstanceKlass(name);
}
// get Java system loader (i.e., application launcher loader)
function systemLoader() {
return sa.sysDict.javaSystemLoader();
}
// iterate system dictionary for each 'Klass'
function forEachKlass(callback) {
var VisitorClass = sapkg.memory.SystemDictionary.ClassVisitor;
var visitor = new VisitorClass() { visit: callback };
sa.sysDict["classesDo(sun.jvm.hotspot.memory.SystemDictionary.ClassVisitor)"](visitor);
}
// iterate system dictionary for each 'Klass' and initiating loader
function forEachKlassAndLoader(callback) {
var VisitorClass = sapkg.memory.SystemDictionary.ClassAndLoaderVisitor;
var visitor = new VisitorClass() { visit: callback };
sa.sysDict["classesDo(sun.jvm.hotspot.memory.SystemDictionary.ClassAndLoaderVisitor)"](visitor);
}
// iterate system dictionary for each primitive array klass
function forEachPrimArrayKlass(callback) {
var VisitorClass = sapkg.memory.SystemDictionary.ClassAndLoaderVisitor;
sa.sysDict.primArrayClassesDo(new VisitorClass() { visit: callback });
}
// 'oop' to higher-level java object wrapper in which for(i in o)
// works by iterating java level fields and javaobject.javafield
// syntax works.
function oop2obj(oop) {
return object(addr2str(oop.handle));
}
// higher level java object wrapper to oop
function obj2oop(obj) {
return addr2oop(str2addr(address(obj)));
}
// Java heap iteration
// iterates Java heap for each Oop
function forEachOop(callback) {
function empty() { }
sa.objHeap.iterate(new sapkg.oops.HeapVisitor() {
prologue: empty,
doObj: callback,
epilogue: empty
});
}
// iterates Java heap for each Oop of given 'klass'.
// 'includeSubtypes' tells whether to include objects
// of subtypes of 'klass' or not
function forEachOopOfKlass(callback, klass, includeSubtypes) {
if (klass == undefined) {
klass = findInstanceKlass("java.lang.Object");
}
if (includeSubtypes == undefined) {
includeSubtypes = true;
}
function empty() { }
sa.objHeap.iterateObjectsOfKlass(
new sapkg.oops.HeapVisitor() {
prologue: empty,
doObj: callback,
epilogue: empty
},
klass, includeSubtypes);
}
// Java thread
// iterates each Thread
function forEachJavaThread(callback) {
var threads = sa.threads;
var thread = threads.first();
while (thread != null) {
if (callback(thread) == false) return;
thread = thread.next();
}
}
// iterate Frames of a given thread
function forEachFrame(javaThread, callback) {
var fr = javaThread.getLastFrameDbg();
while (fr != null) {
if (callback(fr) == false) return;
fr = fr.sender();
}
}
// iterate JavaVFrames of a given JavaThread
function forEachVFrame(javaThread, callback) {
var vfr = javaThread.getLastJavaVFrameDbg();
while (vfr != null) {
if (callback(vfr) == false) return;
vfr = vfr.javaSender();
}
}
function printStackTrace(javaThread) {
write("Thread ");
javaThread.printThreadIDOn(java.lang.System.out);
writeln();
forEachVFrame(javaThread, function (vf) {
var method = vf.method;
write(' - ', method.externalNameAndSignature(), '@bci =', vf.getBCI());
var line = method.getLineNumberFromBCI(vf.getBCI());
if (line != -1) { write(', line=', line); }
if (vf.isCompiledFrame()) { write(" (Compiled Frame)"); }
if (vf.isInterpretedFrame()) { write(" (Interpreted Frame)"); }
writeln();
});
writeln();
writeln();
}
// print Java stack trace for all threads
function where(javaThread) {
if (javaThread == undefined) {
forEachJavaThread(function (jt) { printStackTrace(jt); });
} else {
printStackTrace(javaThread);
}
}
// vmStructs access -- type database functions
// find a VM type
function findVMType(typeName) {
return sa.typedb.lookupType(typeName);
}
// iterate VM types
function forEachVMType(callback) {
var itr = sa.typedb.types;
while (itr.hasNext()) {
if (callback(itr.next()) == false) return;
}
}
// find VM int constant
function findVMIntConst(name) {
return sa.typedb.lookupIntConstant(name);
}
// find VM long constant
function findVMLongConst(name) {
return sa.typedb.lookupLongConstant(name);
}
// iterate VM int constants
function forEachVMIntConst(callback) {
var itr = sa.typedb.intConstants;
while (itr.hasNext()) {
if (callback(itr.next()) == false) return;
}
}
// iterate VM long constants
function forEachVMLongConst(callback) {
var itr = sa.typedb.longConstants;
while (itr.hasNext()) {
if (callback(itr.next()) == false) return;
}
}
// returns VM Type at address
function vmTypeof(addr) {
addr = any2addr(addr);
return sa.typedb.guessTypeForAddress(addr);
}
// does the given 'addr' points to an object of given 'type'?
// OR any valid Type at all (if type is undefined)
function isOfVMType(addr, type) {
addr = any2addr(addr);
if (type == undefined) {
return vmTypeof(addr) != null;
} else {
if (typeof(type) == 'string') {
type = findVMType(type);
}
return sa.typedb.addressTypeIsEqualToType(addr, type);
}
}
// reads static field value
function readVMStaticField(field) {
var type = field.type;
if (type.isCIntegerType() || type.isJavaPrimitiveType()) {
return field.value;
} else if (type.isPointerType()) {
return field.address;
} else if (type.isOopType()) {
return field.oopHandle;
} else {
return field.staticFieldAddress;
}
}
// reads given instance field of VM object at 'addr'
function readVMInstanceField(field, addr) {
var type = field.type;
if (type.isCIntegerType() || type.isJavaPrimitiveType()) {
return field.getValue(addr);
} else if (type.isPointerType()) {
return field.getAddress(addr);
} else if (type.isOopType()) {
return field.getOopHandle(addr);
} else {
return addr.addOffsetTo(field.offset);
}
}
// returns name-value of pairs of VM type at given address.
// If address is unspecified, reads static fields as name-value pairs.
function readVMType(type, addr) {
if (typeof(type) == 'string') {
type = findVMType(type);
}
if (addr != undefined) {
addr = any2addr(addr);
}
var result = new Object();
var staticOnly = (addr == undefined);
while (type != null) {
var itr = type.fields;
while (itr.hasNext()) {
var field = itr.next();
var isStatic = field.isStatic();
if (staticOnly && isStatic) {
result[field.name] = readVMStaticField(field);
} else if (!staticOnly && !isStatic) {
result[field.name] = readVMInstanceField(field, addr);
}
}
type = type.superclass;
}
return result;
}
function printVMType(type, addr) {
if (typeof(type) == 'string') {
type = findVMType(type);
}
var obj = readVMType(type, addr);
while (type != null) {
var itr = type.fields;
while (itr.hasNext()) {
var field = itr.next();
var name = field.name;
var value = obj[name];
if (value != undefined) {
writeln(field.type.name, type.name + '::' + name, '=', value);
}
}
type = type.superclass;
}
}
// define readXXX and printXXX functions for each VM struct/class Type
tmp = new Object();
tmp.itr = sa.typedb.types;
while (tmp.itr.hasNext()) {
tmp.type = tmp.itr.next();
tmp.name = tmp.type.name;
if (tmp.type.isPointerType() || tmp.type.isOopType() ||
tmp.type.isCIntegerType() || tmp.type.isJavaPrimitiveType() ||
tmp.name.equals('address') ||
tmp.name.equals("<opaque>")) {
// ignore;
continue;
} else {
// some type names have ':', '<', '>', '*', ' '. replace to make it as a
// JavaScript identifier
tmp.name = ("" + tmp.name).replace(/[:<>* ]/g, '_');
eval("function read" + tmp.name + "(addr) {" +
" return readVMType('" + tmp.name + "', addr);}");
eval("function print" + tmp.name + "(addr) {" +
" printVMType('" + tmp.name + "', addr); }");
/* FIXME: do we need this?
if (typeof(registerCommand) != 'undefined') {
var name = "print" + tmp.name;
registerCommand(name, name + " [address]", name);
}
*/
}
}
//clean-up the temporary
delete tmp;
// VMObject factory
// VM type to SA class map
var vmType2Class = new Object();
// This is *not* exhaustive. Add more if needed.
// code blobs
vmType2Class["BufferBlob"] = sapkg.code.BufferBlob;
vmType2Class["nmethod"] = sapkg.code.NMethod;
vmType2Class["RuntimeStub"] = sapkg.code.RuntimeStub;
vmType2Class["SafepointBlob"] = sapkg.code.SafepointBlob;
vmType2Class["C2IAdapter"] = sapkg.code.C2IAdapter;
vmType2Class["DeoptimizationBlob"] = sapkg.code.DeoptimizationBlob;
vmType2Class["ExceptionBlob"] = sapkg.code.ExceptionBlob;
vmType2Class["I2CAdapter"] = sapkg.code.I2CAdapter;
vmType2Class["OSRAdapter"] = sapkg.code.OSRAdapter;
vmType2Class["UncommonTrapBlob"] = sapkg.code.UncommonTrapBlob;
vmType2Class["PCDesc"] = sapkg.code.PCDesc;
// interpreter
vmType2Class["InterpreterCodelet"] = sapkg.interpreter.InterpreterCodelet;
// Java Threads
vmType2Class["JavaThread"] = sapkg.runtime.JavaThread;
vmType2Class["CompilerThread"] = sapkg.runtime.CompilerThread;
vmType2Class["SurrogateLockerThread"] = sapkg.runtime.JavaThread;
vmType2Class["DebuggerThread"] = sapkg.runtime.DebuggerThread;
// gc
vmType2Class["GenCollectedHeap"] = sapkg.memory.GenCollectedHeap;
vmType2Class["DefNewGeneration"] = sapkg.memory.DefNewGeneration;
vmType2Class["TenuredGeneration"] = sapkg.memory.TenuredGeneration;
// generic VMObject factory for a given address
// This is equivalent to VirtualConstructor.
function newVMObject(addr) {
addr = any2addr(addr);
var result = null;
forEachVMType(function (type) {
if (isOfVMType(addr, type)) {
var clazz = vmType2Class[type.name];
if (clazz != undefined) {
result = new clazz(addr);
}
return false;
} else {
return true;
}
});
return result;
}
function vmobj2addr(vmobj) {
return vmobj.address;
}
function addr2vmobj(addr) {
return newVMObject(addr);
}
// Miscellaneous utilities
// returns PointerLocation that describes the given pointer
function findPtr(addr) {
addr = any2addr(addr);
return sapkg.utilities.PointerFinder.find(addr);
}
// is given address a valid Oop?
function isOop(addr) {
addr = any2addr(addr);
var oopHandle = addr.addOffsetToAsOopHandle(0);
return sapkg.utilities.RobustOopDeterminator.oopLooksValid(oopHandle);
}
// returns description of given pointer as a String
function whatis(addr) {
addr = any2addr(addr);
var ptrLoc = findPtr(addr);
if (ptrLoc.isUnknown()) {
var vmType = vmTypeof(addr);
if (vmType != null) {
return "pointer to " + vmType.name;
} else {
var sym = closestSymbolFor(addr);
if (sym != null) {
return sym.name + '+' + sym.offset;
} else {
return ptrLoc.toString();
}
}
} else {
return ptrLoc.toString();
}
}