ExamplesIntroduction
This chapter contains snippets of code demonstrating how to call the API. The appendices contain
the unedited samples.
Opening ImagesImage instances are obtained using the javax.tools.diagnostics.FactoryRegistry
class. See for a complete example.
Programs using the API can obtain an Image like so:
Opening an ImageFactoryRegistry uses
javax.imageio.spi.ServiceRegistry
as a registry of all API implementations known to the JVM.
Implementors should ensure that FactoryRegistry is able to see their
ImageFactory by placing their implementation in a jar file that contains a file
called META-INF/javax.tools.diagnostics.image.ImageFactory that contains a line of text that is
the name of the ImageFactory implementation, such as
com.example.dump.ImageFactoryImplDetermining Snapshot Cause
Snapshots can be generated for a variety of reasons. The API can report that a snapshot has been generated
because the JVM received a POSIX type signal, whether it was synchronous or asynchronous.
For example, if a JNI library causes a SIGSEGV when running, this might be detectable
through the API. In most cases, it should be possible through ImageAddressSpace.getCurrentProcess()
and ImageProcess.getCurrentThread() to determine which process and which thread caused
the snapshot.
If a snapshot was generated for a reason other than a POSIX signal being received, then the reason
has to be derived through knowledge of the JVM implementation. For example, if an option was passed to
the JVM to generate a snapshot on entry to a particular method, all of the stack traces could be searched
to determine if that method was present, and therefore probably causing the snapshot. Likewise, detecting
a call to abort() in the native stack would suggest that the snapshot was caused
by a synchronous SIGABRT.
The following examples are extracts from the example program in .
The examples implement the ImageAnalyzer interface, they just need to implement
the analyze(Image) method.
CauseAnalyzer Class declaration
The containing ImageAddressSpace instances are searched for a current process.
It is probable that there will be one address space and one process. If getCurrentProcess()
returns null, there was no current process. Because List is returned, we can
use a for-each loop.
Find Current Process
Once found, the process can be queried for signal information and thread information.
If a signal was raised, ImageProcess.getSignalName() will not be null.
The example reports the signal name and number to the user.
Reporting signal information
The ImageProcess can report the command line. This is the command name
and arguments that were used to start the process. The command and arguments are returned in a single
string, separated by spaces.
The process ID returned by getID() in a String. This is implementation
specific, and so could be in any format, whether that be hexadecimal, decimal or some other arbitrary string.
Process ID and commandline
The program determines which thread caused the snapshot to be generated.
If the current thread isn't null, the thread is identified by ID and its
properties (implementation dependent).
Thread identification
Next, the thread's stack frames are printed out. The code relies on
java.lang.Object.toString() being implemented correctly.
It is expected that the most recent frame will be returned first.
ImageThread stack trace
This section of code relies on the relationship between JavaThread instances
and ImageThread instances to determine which Javatm thread caused the snapshot.
As the relationship is one-way, from Javatm to Image, all of the JavaThread instances
have to be queried. Because JavaThread.getImageThread() might be null,
java.lang.Object.equals is executed against the ImageThread
which we know to be not null. Once the JavaThread is found, its name can be
printed.
JavaThread/ImageThread correlationIdentifying Javatm VM
This example demonstrates how the JVM that generated a snapshot might be identified. The following information
is reported using the image and java APIs:
hostname of the machine the snapshot was generated on.The process ID.The command line.The executable that was running the Javatm program (e.g. "/usr/bin/java").The command line.The loaded native libraries.The version reported by the JVM.The options passed to the JVM.
The complete listing is in .
Like the previous example, this implements ImageAnalyzer. There is some
functionality that is also present in the previous example - it is not repeated here.
Declaration of WhatAnalyzer class
The hostname of the machine where the snapshot was generated is printed out. This isn't information that
is necessarily available in most core dump formats, instead this would normally be recorded by the program.
As it is important to get out as much information as possible, calls to the API are made with
try/catch blocks around each individual method. This is necessary as exceptions should be expected to be raised
under most circumstances.
The stack traces for DataUnavailable are not usually reported as this
is not an error condition. Instead, a message is inserted to indicate that the information is not known.
Getting the hostname
";
e.printStackTrace();
}
System.out.println("Snapshot was generated on " + hostname);
]]>
Here the executable that started the process is reported. This is the executable that
launched the JVM - typically this is the "java" program. Alternatives include
"javac" and "appletviewer". This is the name of the executable the operating system loaded
into memory when creating the process.
Executable name
This code prints out the names of the libraries that were loaded by the process.
This should include any JNI libraries that were configured. Note that it is also
possible to determine where in memory these libraries have been loaded into memory
using the getSections() method. This can
be used to identify where a thread might have crashed.
Process libraries
The following code reports the Javatm VM version. This is implementation dependent,
but is expected to contain more than just the version of Javatm that is supported, but
actually identify which implementation of the JVM it is.
Javatm VM Version
The following code reports the options that were passed to the JVM when it was created.
The options are generated and passed on by the executable the launches Java. Some of these
might be passed on the command line (such as by the "java" executable), but might also include
options taken in from configuration files, as well as being generated by the launcher itself.
Javatm VM OptionsRetrieving Object Fields
This example demonstrates how object instance fields and array elements are accessed
using the API.
The complete listing is in .
The analyzeRuntime method walks over the heaps within the JVM.
While Javatm programmers will be used to the concept of the heap, the API allows a number
of heaps to be accessed in a single JVM. It is expected the different heaps will have
different garbage collection policies and that each heap will be identified with a
descriptive name through JavaHeap.getName(). The number of
heaps and their names is implementation specific, but there must be at least one in a
running JVM.
Iterate over heaps
This section of code retrieves each object from a heap. For API implementations
backed by a core file, the objects will probably be retrieved in order from lowest
address in memory to the highest, but there is no relationship between
JavaObject list indexes and the results of JavaObject.getID()
that can be relied upon by callers of the API.
The JavaObject retrieved has to be tested to see if it is
an array or an ordinary object as they are handled differently.
Iterate over Objects
This method takes a JavaObject and prints out the
values of all of the instance fields (not the static fields).
To identify each object, its ID is used. This is turned into a hex string
using the pointerToHexString(ImagePointer) that is
included in this example.
Print object fields
Each object in an instance of a class, so here the JavaClass is retrieved.
This is equivalient to the following in Java:
Class java.lang.Object.getClass();
Implementors should ensure that the API returns the equivalent JavaClass.
Get the object's type
A class will only report its fields, the superclasses must be retrieved in order to
retrieve their fields. This while loop retrieves each superclass
until the superclass is null, which will be returned by the java.lang.Object
class. The class name is printed out, which should match what java.lang.Class.getName()
would return, except for "." characters being replaced by "/".
Iterate up class hierarchy
This code retrieves each field from a JavaClass.
This is equivalent to the following method in Javatm reflection:
Field[] java.lang.Class.getDeclaredFields()
This should return all fields, even synthetic fields. The DiagnosticException
is a superclass of CorruptDataException and DataUnavailable.
Print out each field
Here the next superclass is retrieved. This will return null if the class
has no superclass, such as java.lang.Object.
The loop is terminated by the
break if the superclass couldn't be retrieved.
get next superclass
This method demonstrates how to print out an instance field.
Note that the JavaObject is passed as it must passed on
to the JavaField for it to retrieve the value of the field
in that instance.
Print fields class
It is not worth printing out the class fields for each instance of the class on the heap,
so the field is tested to see if it is static. The following method call retrieves
the modifiers (public, static, protected, etc.)
from the JavaField and then uses reflection to test for static
being set. Callers of the API should not assume that JavaField.getModifiers()
only returns the bits defined in java.lang.reflect.Modifier - always test
with the appropriate bitmasks or use the functions provided in Modifier.
Testing JavaField.getModifiers()
There are a number of methods provided by JavaField to retrieve
the field value. The most generic is JavaField.get(JavaObject)
which returns an Object.
Getting the value of a field
Object references that were null in the running program are also returned
as null by the API.
JavaField.get() returns null
";
]]>
As JavaField.get(Object) can return
any type, primitive fields values are returned in instances of Number
or Character. For instance, an int would be returned as an
instance of java.lang.Integer. This can't be confused with fields that
are references to java.lang.Integer instances as they would be represented
by JavaObject.
Boxed numbersJavaField.get() is the means by which references to other objects
are also retrieved. This program just retrieves the referred object's class name and its
ID. It is important to remember that the signature of the field is expected to be an appropriate
type for the objects that can be retrieved from it. A field signature would be either the
same type as an object retrieved from it, an interface or super-interface, or a super class.
Retrieving an object reference
The following code tests the object type to see if it is a Javatm String instance.
The JavaClass representing java.lang.String could be cached
and compared against the objects classes, but instead we compare against the name of the object's class.
The method JavaField.getString() is used to retrieve the JavaObject
as an instance of java.lang.String in the running JVM.
Retrieving a string field
This method deals only with arrays, which are treated differently from ordinary objects when
retrieving their contents. Note that arrays don't have a field called "length".
Method for printing out array contents
All instances of JavaObject have a JavaClass
with a name. For arrays, this follows the JNI conventions. An integer array would be
called "[I", whereas an array of strings would be called "[Ljava/lang/String;".
All objects have classes
Each array describes the number of elements it contains. It is
important to call getArraySize() and not
getSize() as the latter returns the size
of the object on the heap.
Get number of array elements
An array's class should be able to report the type of its elements.
This call is used to determine the type of array to receive the contents of
the array.
Getting the type of the array elements
Arrays elements are not accessed on an individual basis. Instead,
their contents are copied to real arrays.
This code demonstrates that there are JavaClass for
primitive types, in the same way there is in reflection. These names
are used to create primitive arrays of the correct type. Javatm reflection
functions in the same way.
Creating array of correct type
If an array is not an array of primitives, it must be an array
of objects. As there is no means of converting a JavaObject
into a "real" object, an array of JavaObject instance is returned.
Multidimensional arrays are returned as arrays of JavaObject instances
that are themselves arrays. The example code shows how a array to receive object arrays is allocated.
Array of JavaObjects as destination
The method JavaObject.arraycopy is used to copy array elements
in the same way as java.lang.System.arraycopy().
Implementors and those writing applications using this method call should take care as arrays
can be extremely large, potentially larger than the JVM's heap size. If a fraction of an array
is asked for, that is all that should be allowed in memory.
Copying array contents
This code prints out the contents of the array elements. The java.lang.Array.get()
method is used to retrieve elements from the array in a generic fashion. If null is
retrieved, that is printed, otherwise if it is an object the type and address of the object is printed
and failing that it must be an autoboxed primitive that can be printed out using its toString().
The CorruptDataException is caught within the loop to allow the printing to continue
even if some of the elements can't be located.
Printing out array elements