Serviceability Agent (SA) provides Java API and tools to diagnose HotSpot Virtual Machine and Java apps running on it. SA is a snapshot debugger -- can be used to observe state of a frozen java process or java core dump.
There are two application programmer interfaces (APIs) for SA:
Traditionally, platform debuggers such as dbx, gdb and Solaris mdb (Module Debugger), provide a scripting language interface. Scripting language interface provides easy-to-use, dynamically typed interface to access data structures from debuggee. dbx and mdb even allow user to write C/C++ modules to extend the scripting language commands.
SA provides SOQL - Simple Object Query Language -- a SQL-like query language to access Java heap as an object database. SA's main GUI (HSDB) also exposes scripting interface of underlying debugger such as dbx, windbg. But to use this interface, user has to learn scripting interface of multiple debugger back-ends such as dbx, windbg. And these scripting interfaces are 'raw' in the sense that no java state is exposed -- only C/C++ state of VM is exposed. Higher level SA services are not available through scripting interface.
jsdb -- JavaScript Debugger attempts to provide JavaScript interface to SA. jsdb provides
Function/Variable | Description |
---|---|
address(jobject) | function that returns the address of the Java object as a string | classof(jobject) | function that returns the JavaScript object that represents class object of the Java object | dumpClass(jclass,[dir]) | function that writes .class for the given Java Class. Optionally (second arg) accepts the directory where the .class has to be written. | help() | function that prints help message for global functions and objects |
identityHash(jobject) | function that returns the identity hashCode of the Java object |
mirror(jobject) | function that returns a local mirror of the Java object. |
load([file1, file2,...]) |
function that loads zero or more JavaScript file(s). With no arguments, reads |
object(string) | function that converts a string address into Java object |
owner(jobject) | function that returns the owner thread of this monitor or null |
sizeof(jobject) | function that returns the size of Java object in bytes |
staticof(jclass, field) | function that returns the value of given field of the given Java class |
print(expr1, expr2,...) | function that prints zero or more JavaScript expressions after converting those as strings |
println(expr1, expr2..) | function that same as print, but prints a newline at the end |
read([prompt]) | function that reads a single line from standard input |
quit() | function that quits the interactive load call as well as the shell |
jvm | variable -- a JavaScript object that represents the target jvm that is being debugged |
jvm object supports the following read-only properties.
Property name | Description |
---|---|
threads | array of Java threads from the debuggee |
heap | object representing the heap of the debuggee |
type | string value that is either "Server" or "Client" or "Core" -- the flavour of debuggee VM |
bootClassPath | string value of bootclasspath of the debuggee |
cpu | string value of cpu on which the debuggee runs/ran |
sysProps | name-value pairs (JavaScript associative array) of Java System properties of the debuggee |
addressSize | int value -- 32 for 32 bit debuggee, 64 for 64 bit debuggee |
os | string value of OS on which the debuggee runs/ran |
buildInfo | internal build info string from debuggee |
flags | name-value pairs (JavaScript associative array) of JVM command line flags of the debuggee |
classPath | string value of classpath of the debuggee |
userDir | string value of user.dir System property of the debuggee |
heap object represents Java heap of the debuggee VM
Function or property name | Description |
---|---|
capacity | byte size of capacity of the heap |
used | byte size of used portion (of live objects) of the heap |
forEachObject(func, [class], [include subtypes -- true|false]) | This function accepts a callback function 'func' and optionally class name and boolean arguments. This function calls the callback for each Java object in the debuggee's heap. The optional class argument may be used to receive objects of given class only. The third arguments specifies whether to include objects of subtype of given class [or interface] or not. The default value of class is "java.lang.Object" and and that of the third argument is true. i.e., by default all objects are included. |
forEachClass(func, [initiating loader -- true|false]) | This function accepts a callback function 'func'. This function iterates through the classes of the debuggee and calls the callback for each class. The second parameter tells whether to pass initiating loader to the iterator callback or not. |
From a given Java object, we can access all fields of the Java object by usual '.' operator. i.e., if you got a Java object called 'o' of type java.lang.Thread from debuggee, you can access 'stackSize' field by o.stackSize syntax. Similarly, length of Java array objects can be accessed by length property. And array indexing follows usual syntax. i.e., n'th element of array 'a' is accessed by a[n].
This is a JavaScript array of Java threads of the debuggee. As usual, 'length' property tells the number of threads and individual threads may be accessed by index operator -- i.e, jvm.threads[0] returns the first thread.
In addition to the fields of java.lang.Thread (or subclass) fields, thread objects have two additional properties.
Property name | Description | thisObject | Object representing 'this' of the current frame [will be null for static methods] |
---|---|
locals | name-value pairs of local variables [JavaScript associative array] |
line | Java source line number at which the frame is executing |
bci | byte code index of the bytecode that the frame is executing |
thread | thread to which this frame belongs |
method | Java method that the frame is executing |
method object represents a Java method of debuggee
Property name | Description |
---|---|
isStatic | boolean - true for static methods and false for non-static methods |
isSynchronized | boolean - true for synchronized methods and false for non-synchronized methods |
isNative | boolean - true for native methods and false for non-native methods |
isProtected | boolean - true for protected methods and false for non-protected methods |
isPrivate | boolean - true for private methods and false for non-private methods |
isSynthetic | boolean - true for Javac generated synthetic methods and false for non-synthetic methods |
isPackagePrivate | boolean - true for package-private methods and false for non-package-private methods |
isPublic | boolean - true for public methods and false for non-public methods |
holder | an object that represents Class that contains this method |
signature | string -- signature of this method |
isObsolete | boolean - true for obsolete (hotswapped) methods and false for non-obsolete methods |
isStrict | boolean - true for strictfp methods and false for non-strictfp methods |
isFinal | boolean - true for final methods and false for non-final methods |
name | string - name of this method |
A class object represents loaded Java class in debuggee VM. This represents java.lang.Class instance in the debuggee. This is type of return value of classof global function. Also, method.holder property and field.holder are of this type.
Property name | Description |
---|---|
name | name of this class |
superClass | class object representing super class of this class |
isArrayClass | boolean -- is the current class an array class? |
isStatic | boolean -- is the current class static or not |
isInterface | boolean -- is the current class an interface |
isAbstract | boolean -- is the current class abstract or not |
isProtected | boolean -- is the current class protected or not |
isPrivate | boolean -- is the current class private or not |
isPackagePrivate | boolean -- is the current class package private or not |
isSynthetic | boolean -- is the current class synthetic or not |
classLoader | object that represents ClassLoader object that loaded the current class |
fields | array of static and instance fields of the current class |
protectionDomain | protection domain to which current class belongs |
isPublic | boolean -- is the current class public or not |
signers | array of signers for current class |
sourceFile | string -- name of the source file for current class |
interfaces | array -- interfaces implemented by current class |
isStrict | boolean -- is the current class strictfp or not |
methods | array of methods (static and instance) of the current class |
isFinal | boolean -- is the current class final or not |
statics | name-value pairs (JavaScript associate array) of static fields of the current class |
field represents a static or instance field of some class in debuggee
Property name | Description |
---|---|
isStatic | boolean -- is this field a static field? |
holder | class that owns this field |
signature | string signature of this field |
isProtected | boolean - is this field a protected field or not? |
isPrivate | boolean - is this field a private field or not? |
isSynthetic | boolean - is this javac generated synthetic field or not? |
isPackagePrivate | boolean - is this field a package private field or not? |
isTransient | boolean - is this field a transient field or not? |
isFinal | boolean - is this field final or not? |
name | string - name of this field |
isPublic | boolean - is this field public or not? |
jsdb engine looks for initialization script file named jsdb.js in user's home directory. If found, it loads just after attaching to debuggee but before printing prompt for user's input. User can assume that s/he can access debuggee VM state during initialization script.
jvm.sysProps.toString()
jvm.flags.toString()
// associate array to hold histogram
var histo;
function func(obj) {
var classname = classof(obj).name;
if (histo[classname] == undefined) {
// first time we are visiting this class type
histo[classname] = 1;
} else {
histo[classname]++;
}
}
// iterate through java heap calling 'func' for each object
jvm.heap.forEachObject(func);
// print the histogram
for (i in histo) {
println('number of instances of ', i, ' = ', histo[i]);
}
function printStackTrace(t) {
println(t.name);
println('');
for (i in t.frames) {
println(t.frames[i]);
}
println('');
}
// walk through the list of threads and call printStackTrace
// for each thread
for (o in jvm.threads) {
printStackTrace(jvm.threads[o]);
}
function dump(cl) {
if (!cl.isArrayClass && cl.classLoader != null) {
// not an array class and a non-bootstrap class
// create .class files in e:\tmp dir
dumpClass(cl, "e:\\tmp);
} else {
println("skipping bootstrap class ", cl.name);
}
}
// walk thru heap and call callback for each java.lang.Class instance
jvm.heap.forEachObject(dump, "java.lang.Class");
function printFile(f) {
// construct a mirror java.io.File here and
// print absolute path here
println(mirror(f).getAbsolutePath());
}
jvm.heap.forEachObject(printFile, "java.io.File");
var threadClass = classof("java.lang.Thread");
for (i in threadClass.statics) {
println(i, '=', threadClass.statics[i]);
}
Low level jsdb interface works by JavaScript-to-Java (previously known as "LiveConnect") interface provided by Rhino JavaScript engine.
This object provides short names for SA package names. For eg. instead of writing Packages.sun.jvm.hotspot.memory, we can write sapkg.memory.
This object contains all SA singleton objects such as VM, Universe, SymbolTable, SystemDictionary, ObjectHeap, CollectedHeap, Debugger, CDebugger (if available), Interpreter, TypeDataBase and Threads. For eg. to access SymbolTable of Java debuggee, we can use sa.symbolTable. User can execute the following code to get fields of this object.
for (i in sa) {
println(i);
}
Java objects of debuggee are represented by different script wrappers in high level interface. In the low-level interface these are instances of SA Oop class or its' subclass. To move b/w low-level and high-level interfaces the following functions may be used
for(i in object) { println(i); }
for(i in this) { println(i); }