Most of the work done by the analyzer in this example relates to examining the contents of various WAS data structures, for which we use special pre-defined analyzer from the library, such as
ObjectWrapper. In order to do their work, these analyzers call lower-level functions from the
DTFJ interface that is provided by the JDK as a standardized interface to access the contents of a dump. In effect, this library of analyzers essentially provides a level of abstraction on top of the DTFJ interface, with higher-level specialized functions.
But sometimes, we may want to access low-level functions from the DTFJ interface directly, to access some combination of information that is not pre-defined in some analyzer from the library, or simply to access some information that is so basic and straightforward that there would be no point writing a specialized analyzer to encapsulate it. For this example, let us extend our WASThreadPoolsSample analyzer to count the total number of Java threads in the JVM being analyzed, and figure out how many of these Java threads are not part of a WAS thread pool:
private int countJVMThreads() {
JavaRuntime runtime = getContext().getCurrentJavaRuntime();
Iterator it = runtime.getThreads();
int count = 0;
while (it.hasNext()) {
it.next();
count++;
}
return count;
}
We add the method
countJVMThreads() in the class implementing the WASThreadPoolsSample analyzer. And in the body of the
produceReport() method, we add the line
out.printField("Number of JVM threads not in a pool", countJVMThreads() - numberOfAllocatedThreads);
The key elements in this code are as follows:
The
context associated with each analyzer, obtained through a call to
getContext(), provides us with the necessary starting point to access the DTFJ-level information. The
getCurrentJavaRuntime() method of the
context returns a reference to the DTFJ
JavaRuntime object that represents the runtime of the JVM being analyzed. From this
JavaRuntime, we can call other DTFJ methods, in this case
getThreads() which returns the list of all Java threads.
The
context contains a few other methods to gain access to other key entities in the DTFJ object hierarchy:
- getCurrentImage() returns the DTFJ Image, which represents the entire contents of the whole dump
- getCurrentAddressSpace() returns a DTFJ ImageAddressSpace reference, possibly one of several address spaces within this image / dump.
- getCurrentProcess() returns a DTFJ ImageProcess reference, possibly once of several processes within the current address space
- ... etc...
In general, once you get a hold of one of these DTFJ starting points in your analyzer, you can invoke any applicable DTFJ methods on it, use them to obtain DTFJ references to other types of items in the JVM, call further DTFJ methods on them, etc. as necessary to extract any desired information.
The description of calls like
getCurrentJavaRuntime(),
getCurrentProcess(), etc. raises the question of what does "current" mean in this context. This concept is introduced because, in some cases and on some platforms (esp. z/OS), a single dump (known in DTFJ as an
Image) may contain multiple address spaces (DTFJ
ImageAddressSpace), which in turn may contain multiple processes (DTFJ
ImageProcess), which in turn may contain multiple runtimes (DTFJ
ManagedRuntime or
JavaRuntime). When this is the case, the analyzer actually operates in the context of one particular tuple of address space / process / runtime, represented by the
context object associated with that analyzer. If necessary, the same analyzer may be executed multiple times, each time with a different
context corresponding to a different tuple. See the javadoc for the
IAnalyzerContext interface for further details about this mechanism.
Generating Reports with a Summary and a Details Section