Express (Distributed operating systems), v8.0 > Troubleshoot and support > Analyzing application server Java system dumps with the IBM Monitoring and Diagnostic Tools for Java - Dump Analyzer > Write Dump Analyzer modules for WAS diagnostics - Tutorial
Tutorial: Writing Dump Analyzer modules for WAS diagnostics
A Simple Analyzer to Examine Data Structures
Let's start writing a simple analyzer that scans through the data structures that represent all the thread pools in a WAS process, and prints key information about each pool:
public class WASThreadPoolsSample1 extends WASAnalyzerBase implements IReport {public IAnalysisReport produceReport() { IAnalysisReport out = allocateReport(null); ObjectWrapperCollection pools = ObjectWrapperCollection.getObjectInstances(getContext(), "com/ibm/ws/util/ThreadPool"); for (int index = 0; index < pools.size(); index++) { ObjectWrapper pool = (ObjectWrapper) pools.get(index); pool.printValueAtPath(out, "Pool name", "name"); pool.printValueAtPath(out, "Min size", "minimumPoolSize_"); pool.printValueAtPath(out, "Max size", "maximumPoolSize_"); pool.printValueAtPath(out, "Current size", "poolSize_"); pool.printValueAtPath(out, "List of Threads", "threads_"); out.printLiteral(""); } return out; } }
Here is a fragment of the output from this analyzer, run on a small WAS process dump:
...Pool name: "WLMMonitorSleeper" Min size: 0 Max size: 10 Current size: 1 List of Threads: java/util/HashMap@0x01167330
Pool name: "SoapConnectorThreadPool" Min size: 3 Max size: 5 Current size: 2 List of Threads: java/util/HashMap@0x01F910C0
Pool name: "WebContainer" Min size: 10 Max size: 50 Current size: 0 List of Threads: java/util/HashMap@0x02149D50
...
Let's examine the key aspects of this analyzer:
Analyzer structure
The analyzer is a simple Java class, defined as in the previous example of the Hello World Analyzer. It's main purpose is to generate a report, so it contains a produceReport() method that will be invoked by the tool framework when that analyzer is requested, and that returns a report containing the desired information.
Unlike the Hello World Analyzer, this analyzer needs to do some work inside its produceReport() method to actually find the information that it wants to produce.
Enumerating all instances of the ThreadPool data structure
We happen to know that, in WAS, each thread pool is represented by a data structure of class com.ibm.ws.util.ThreadPool. So we need some code to look for all instances of that class present inside the dump:
ObjectWrapperCollection pools = ObjectWrapperCollection.getObjectInstances(getContext(), "com/ibm/ws/util/ThreadPool");ObjectWrapperCollection is a special analyzer, part of the library of standard analyzers shipped with the Dump Analyzer tool, whose purpose is to find collections of objects: all the instances of a given class, or all the subclasses of a given class, etc. Here, we use the static factory method ObjectWrapperCollection.getObjectInstances(), which returns an instance of the ObjectWrapperCollection analyzer that represents the desired list of instances. The parameters are as follows:
- getContext() is needed internally to give the analyzer some context information, e.g. a reference to the dump being analyzed. The details of this are not important for this tutorial, and can be found in the full API documentation for the Dump Analyzer tool. Here, suffice it to say that we invoke the getContext() method implemented by the WASAnalyzerBase class for our analyzer, and pass that same context value to initialize the new ObjectWrapperCollection analyzer that we want to create. We will find this same pattern everywhere: whenever we invoke a particular analyzer as part of the operation of some other analyzer, we must pass it a context, which is typically the same context that is associated with the analyzer that is making the invocation.
- com/ibm/ws/util/ThreadPool is the name of the class for which we want to find all the instances. Note that we use the internal Java representation of class names, with a "/" as the separator character rather than a ".".
ObjectWrapperCollection is an analyzer, that happens to implement the java.util.Collection from the standard Java Collections framework. So we can use that interface to enumerate and extract the items from the collection, i.e. each of the instances of com/ibm/ws/util/ThreadPool that are found in the dump.
Each item in the collection is represented by another standard analyzer from the library, of type ObjectWrapper. Each instance of the ObjectWrapper analyzer represents one particular object instance from the dump being analyzed.
So we iterate over these instances simply as follows: (we could also have used a Java Iterator over the collection)
for (int index = 0; index < pools.size(); index++) { ObjectWrapper pool = (ObjectWrapper) pools.get(index); // ... do whatever we need to with each "pool" object }
Print fields from one object instance
So now, we have a local variable pool that references an ObjectWrapper analyzer that represents one particular instance of a com/ibm/ws/util/ThreadPool object from the dump being analyzed. We want to print the contents of that object, i.e. print the value of its various fields:
pool.printValueAtPath(out, "Pool name", "name"); pool.printValueAtPath(out, "Min size", "minimumPoolSize_"); pool.printValueAtPath(out, "Max size", "maximumPoolSize_"); pool.printValueAtPath(out, "Current size", "poolSize_"); pool.printValueAtPath(out, "List of Threads", "threads_");This code is pretty self-explanatory. The ObjectWrapper analyzer exports a method printValueAtPath(), that looks for a particular field in the target object being represented by this ObjectWrapper and prints the value of that field. The arguments are as follows:
- out: a reference to the report object in which to write the value of the field
- a label to describe this field in the report
- a string that must match exactly the name of the field in the target object.
In this case, we are printing five fields named "name", "minimumPoolSize_", "maximumPoolSize_", "poolSize_" and "threads_"). We picked those fields by looking at the definition of com/ibm/ws/util/ThreadPool, either from the source code or by examining it with another analyzer or command in the Dump Analyzer tool itself, and we decided that these five fields were the ones that we were particularly interested in for this new analyzer.
If the value of a given field is a String or one of the primitive Java types (int, boolean, etc.) or Integer, Boolean, etc., that value is printed normally.
If the value of a given field is an object reference, the printValueAtPath() method prints a standard representation for all object references, like java/util/HashMap@0x01167330 in the example above. This means that this particular field contains a reference to a HashMap object that happens to be at the address 0x01167330 in the dump being analyzed. In the following sections of this tutorial, we will see how we can follow that reference to see the fields inside that referenced object itself, and other that it in turn references, etc.
If the name of the field specified in printValueAtPath() does not correspond to a valid field defined in the target object, then this method prints an error indication like [ <analyzererror>] in lieu of the expected value of the field.
Notes:
- There are several other methods and variations similar to printValueAtPath(). We will encounter several of them in other sections of this tutorials. For the complete list, refer to the API documentation for the ObjectWrapper analyzer.
- What if I want to print the address of the target object itself (e.g. com/ibm/ws/util/ThreadPool@0x022DE1F8) instead of the value of one of its fields? I can do that by invoking printValueAtPath() with a null string ("") for the field name.
Separating the output for each of the thread pools
The code in this analyzer iterates over all the thread pool objects, and prints some fields for each object. We need a way to mark in the report when the information for one thread pool object is ended and we start the information for the next object.
In this simplest example, we use the construct
out.printLiteral("");to insert a blank line in the report. In the next section of this tutorial, we will learn some more powerful ways to control the format the report.
Next Topic
Write and Formatting Reports