Kato > Index > Object Fields example
Added by Stuart Monteith, last edited by Stuart Monteith on Apr 08, 2009

Object fields example

What follows is some example code that demonstrates accessing all of the objects on the heap, and prints out the field values as well as all of the array elements.

ObjectFields.java
/*******************************************************************************
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package org.apache.kato.example;

import java.lang.reflect.Array;
import java.util.Iterator;

import org.apache.kato.image.CorruptData;
import org.apache.kato.image.CorruptDataException;
import org.apache.kato.image.DTFJException;
import org.apache.kato.image.ImagePointer;
import org.apache.kato.image.MemoryAccessException;
import org.apache.kato.java.JavaClass;
import org.apache.kato.java.JavaField;
import org.apache.kato.java.JavaHeap;
import org.apache.kato.java.JavaObject;
import org.apache.kato.java.JavaRuntime;

/**
 * This examples walks over all objects on the heap and prints the values
 * of all of the fields, and the contents of all elements of all the arrays.
 * 
 * The example starts with walkObjectFields(JavaRuntime), so it's not a complete
 * example.
 * 
 * 
 * The following API interfaces are used:
 * 	JavaRuntime
 * 		JavaHeap
 * 			JavaObject
 * 				JavaClass
 * 					JavaField
 * 
 * It is useful to consider the classes as being analogues of the Java reflection classes.
 * The equivalence is:
 * 
 * JavaObject	java.lang.Object
 * JavaClass	java.lang.Class
 * JavaField	java.lang.reflect.Field
 * 
 * Here is some example output:

JavaObject @ 0x100f7270
	java/lang/UnsatisfiedLinkError:
	  java/lang/LinkageError:
	    java/lang/Error:
	      java/lang/Throwable:
	        Ljava/lang/Object; backtrace = [Ljava/lang/Object;: @ 0x100f70d8
	        Ljava/lang/String; detailMessage = java/lang/String: @ 0x100f7108java/lang/String: @ 0x100f7108 = "/home/monteith/IBMJava2-142/jre/bin/libCrash.so: /home/monteith/IBMJava2-142/jre/bin/libCrash.so: cannot open shared object file: No such file or directory"
	        Ljava/lang/Throwable; cause = <null reference>
	        [Ljava/lang/StackTraceElement; stackTrace = <null reference>
	        java/lang/Object:
JavaObject @ 0x100f7290
	java/lang/String:
	  [C value = [C: @ 0x100f72b0
	  I offset = 0
	  I count = 30
	  I hash = 0
	  java/lang/Object:
JavaObject @ 0x100f72b0
	[C[30] = {
		j,
		a,
		v,
		a,
		.,
		l,
		a,
		n,
		g,
		.,
		U,
		n,
		s,
		a,
		t,
		i,
		s,
		f,
		i,
		e,
		d,
		L,
		i,
		n,
		k,
		E,
		r,
		r,
		o,
		r,
	};

 */
public class ObjectFields {
	
	/**
	 * Given a JavaRuntime, print out all of the objects on the heap.
	 * 
	 * @param jr JavaRuntime to get objects from
	 */
	public void walkObjectFields(JavaRuntime jr) {
		
		/* First get the iterator for all of the heaps.
		 * A JVM's heap may split across different heaps, e.g.
		 * nurseries, system heap, large object heap.
		 */
		Iterator heaps = jr.getHeaps();
		
		
		/* What follows is a typical pattern, 
		 * iterate, check type, handle data or handle corrupt data,
		 * continue.
		 */
		while (heaps.hasNext()) {
			Object next = heaps.next();
			
			// This is the normal case.
			if (next instanceof JavaHeap) {
				walkHeap ((JavaHeap) next);
			} 
			// As per the JavaDoc, expect CorruptData.
			else if (next instanceof CorruptData) {
				// We have a string to print out and an address.
				System.err.println("Corrupt Heap instance \"" + next + "\"" +
						pointerToHexString(((CorruptData)next).getAddress()));
				continue;
			}
			
		} // while (heaps.hasNext())		
	}
	
	/**
	 * Walks over all objects on the heap.
	 * 
	 * @param heap JavaHeap to iterate over.
	 */
	public void walkHeap(JavaHeap heap) {
		Iterator objects = heap.getObjects();
		
		// Follow same pattern as walkObjectFields
		while (objects.hasNext()) {
			Object next = objects.next();
			
			/* The heap has only JavaObjects on it. Not JavaClasses,
			 * but JavaObjects of type java/lang/Class will be encountered.
			 */
			if (next instanceof JavaObject) {
				JavaObject jObject = (JavaObject) next;
				
				/* Some objects are arrays, while they are JavaObjects.
				 * They are handled differently.
				 */
				try {
					if (jObject.isArray()) {
						walkArray (jObject);
					} else {
						walkObject (jObject);
					}
					
					// Even determining whether or not the JavaObject is an array has difficulties.
				} catch (CorruptDataException e) {
					System.err.println("Corrupt data exception calling jObject.isArray() at "+
							pointerToHexString(jObject.getID()));
					e.printStackTrace();
				}
 			} 
			
			/* Iterators return a mix of objects - either what you expected
			 * or a CorruptData object.
			 */
			else if (next instanceof CorruptData) {
				// We have a string to print out and an address.
				System.err.println("Corrupt Heap instance \"" + next + "\"" +
						pointerToHexString(((CorruptData)next).getAddress()));
				continue;
			}
				
				
		} // while (objects.next()
	}
	
	/**
	 * Prints out all of the values of the fields in an object, along with
	 * identifying information of the type itself.
	 * 
	 * <br/>To print out the values of an objects fields, we do the following:
	 * 
	 * <ol>
	 * <li>Get the object's type - a JavaClass.</li>
	 * <li>Retrieve the fields (JavaField) from that JavaClass.</li>
	 * <li>Call the JavaField's get methods, passing the JavaObject, printing out the values.</li>
	 * <li>Get the type's superclass (another JavaClass), if it's not null, repeat from 2.</li>
	 * </ol>
	 * 
	 * @param jObject A JavaObject
	 */
	public void walkObject(JavaObject jObject) {
	
		
		// Just identify the object by its ID - this would the address on the heap.
		System.out.println("JavaObject @ " + pointerToHexString(jObject.getID()));
		// Handle indentation.
		String prefix = "\t";
		
		/* Get the type of this object.
		 * A common mistake is to call "Class Object.getClass()".
		 * 
		 * If we can't get the type, print error and return - there is nothing
		 * to be done here. It's important that the iterator either finishes or
		 * continues past the bad object.
		 */		
		JavaClass clazz;
		try {
			clazz = jObject.getJavaClass();
		} catch (CorruptDataException e) {
			System.err.println(prefix+"Error getting JavaClass");
			e.printStackTrace();
			return;
		}
		
		/* The fields in a class are only the fields for that class.
		 * The superclasses fields must retreived from the superclasses. 
		 * 
		 * Some classes don't have a superclass - null is used to indicate this.
		 */
		while (clazz != null) {
			// print out the name of the class and the the fields.
			try {
				System.out.println( prefix + clazz.getName() +":");
				prefix += "  ";
			
				Iterator fields = clazz.getDeclaredFields();
			
				/* Print out all fields for this class.
				 */
				while (fields.hasNext()) {
					Object nextField = fields.next();
					
					if (nextField instanceof JavaField) {
						printField(prefix, (JavaField) nextField, jObject);						
					} else if (nextField instanceof CorruptData) {
						System.err.println("Error getting field. " + nextField);
					}
				} // while (fields.hasNext())
			} catch (DTFJException e) {
				System.err.println("Error printing out fields.");
				e.printStackTrace();
			}
			
			// Get the next superclass.
			try {
				clazz = clazz.getSuperclass();
			} catch (CorruptDataException e) {
				
				e.printStackTrace();
				break;
			}
			
		} // while (class != null)
	}
	
	/**
	 * Print out the content of one field.
	 * Only prints out instance fields. Will return if a
	 * static field is passed.
	 * 
	 * @param prefix Pad out message
	 * @param field The field to print
	 * @param object The object to print out
	 * @throws CorruptDataException if something goes wrong
	 * @throws MemoryAccessException 
	 */
	private void printField(String prefix, JavaField field, JavaObject object) throws CorruptDataException, MemoryAccessException {

		/* Modifier is fully qualified to indicate that some constants are not held by the API,
		 * but reused from the existing reflection API.
		 * This is testing the field to see if it is static. The getModifiers method is
		 * held within the JavaMember interface. Here the resemblance to Java reflection is more
		 * pronounced.
		 */
		if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
			return;
		}
		
		/* Get the value of a field. This call will suit for all
		 * field types, primitives and references.
		 * A java.lang.Integer instance would be returned as a JavaObject, not as an Integer,
		 * but a primitive int field will be returned as an Integer.
		 * There are methods that can be used to return the primitives directly, the
		 * JavaMember.getSignature() methods would have to be used in order to determine the
		 * field's type.
		 * 
		 */
		Object fieldValue = field.get(object);
		
		String valueString = "";
		
		// Format the field's value. 
		if(fieldValue == null) {
			valueString = "<null reference>";
			
			// Most of the primitive fields can be handled as Number instances.
		} else if (fieldValue instanceof Number) {
			valueString = fieldValue.toString();
		} else if (fieldValue instanceof Character) {
			valueString = "`" + (Character)fieldValue + "'";
		} else if (fieldValue instanceof Boolean) {
			valueString = ((Boolean) fieldValue).booleanValue() ? "true" : "false";
		} else if (fieldValue instanceof JavaObject) {
			// Note how we have to get an instance of the object to know anything about it.
			JavaObject reference = (JavaObject) fieldValue;
			
			// classname: @ 0xadddress
			valueString = reference.getJavaClass().getName() + ": @ " + pointerToHexString(reference.getID());
			
			/* We can get the actual String as it would appear if printed on the running JVM using
			 * the JavaField.getString() method. 
			 * To do this, we have to identify the object referenced as a java.lang.String.
			 * We use a string comparision against the referenced object's class name rather than using
			 * JavaField.getSignature(), as that could simply be a java.lang.Object reference.
			 * 
			 * This is the only means of translating a JavaObject into a String instance without
			 * examining the referenced JavaObject fields using the API.
			 */
			if ("java/lang/String".equals(reference.getJavaClass().getName())) {
				valueString += valueString +" = \"" + field.getString(object) + "\"";
			}
			
		}
		// The JavaField.get() method doesn't return CorruptData - it throws CorruptDataException and
		// MemoryAccessException
		
		/* Print out the value of the field, prefixed by its signature and name.
		 * The signature will come out in the internal, compact format. This could
		 * be expanded with more code into Java language style.
		 * 
		 * For example, a string would be "Ljava/lang/String;", a string array
		 * would be "[Ljava/lang/String;", a byte would be "B", a boolean array
		 * would be "[Z".
		 * 
		 */ 
		System.out.println(prefix + field.getSignature() + " " + 
				field.getName() + " = " + valueString);		
	}

	/**
	 * Print out the contents of an array.
	 * 
	 * @param jObject JavaObject of an array.
	 */
	public void walkArray(JavaObject object) {
		// Just identify the object by its ID - this would the address on the heap.
		System.out.println("JavaObject @ " + pointerToHexString(object.getID()));
		// Handle indentation.
		String prefix = "\t";
		String className;
		
		JavaClass clazz;
		try {
			clazz = object.getJavaClass();
		} catch (CorruptDataException e) {
			System.err.println("Unable to determine array's JavaClass. aborting");
			e.printStackTrace();
			return;
		}
		
		// The class name is needed to determine the element types
		try {
			className = clazz.getName();
		} catch (CorruptDataException e) {
			System.err.println("Error getting Array class name.");
			e.printStackTrace();
			return;
		}

		
		int arraySize = 0;

		/* This gets the number of elements in the array.
		 * It should not be confused with JavaObject.getSize() which
		 * is the size of the array on the heap.
		 */ 
		try {
			arraySize = object.getArraySize();
		} catch (CorruptDataException e) {
			System.err.println("Unable to get object size.");
			e.printStackTrace();
			return;
		}		
		
		String componentName;
		try {
			componentName = clazz.getComponentType().getName();
		} catch (CorruptDataException e) {
			System.err.println("Unable to determine component type name. Quitting.");
			e.printStackTrace();
			return;
		}
		
		Object arrayCopy;
		
		/* Create an instance of the array that JavaObject.arraycopy()
		 * will copy into. Must be of correct type.
		 * 
		 * There exist classes that directly represent the primitive types.
		 * See Javadoc for java.lang.Class.
		 * 
		 * i.e. an actual class called "int", not "java/lang/Integer".
		 */
		if ("boolean".equals(componentName)) {
			arrayCopy = new boolean[arraySize];
		} else if ("byte".equals(componentName)) {
			arrayCopy = new byte[arraySize];
		} else if ("char".equals(componentName)) {
			arrayCopy = new char[arraySize];
		} else if ("short".equals(componentName)) {
			arrayCopy = new short[arraySize];
		} else if ("int".equals(componentName)) {
			arrayCopy = new int[arraySize];
		} else if ("long".equals(componentName)) {
			arrayCopy = new long[arraySize];
		} else if ("float".equals(componentName)) {
			arrayCopy = new float[arraySize];
		} else if ("double".equals(componentName)) {
			arrayCopy = new double[arraySize];
		} else {
			// Anything element that is not a primitive must be a reference type.
			arrayCopy = new JavaObject[arraySize];
		}
							
		/* Copy the contents of the JavaObject array into the array we prepared earlier.
		 * This is the only way to get the values of an array in a dump.
		 */
		try {
			object.arraycopy(0, arrayCopy, 0, arraySize);
		} catch (CorruptDataException e) {
			e.printStackTrace();
			return;
		} catch (MemoryAccessException e) {
			e.printStackTrace();
			return;
		} catch (IllegalArgumentException e){
			e.printStackTrace();
			return;
		} catch (IndexOutOfBoundsException e) {
			e.printStackTrace();
			return;
		}
		
		/* Go through every element in the copy of the array and print out its contents.
		 * This example uses java.lang.Array.get() to make this easy to handle generically.
		 * 
		 * Object references - JavaObjects - are only printed out at the type name @ an address.
		 * We don't print out subarrays - they will be encountered later in the heap anyhow.
		 */
		System.out.println("\t" + className + "[" + arraySize +"] = {");
		for (int cnt=0; cnt < arraySize; cnt++) {
			Object obj = Array.get(arrayCopy, cnt);
			
			if (obj == null) {
				System.out.println("\t\tnull,");
			} else if (obj instanceof JavaObject) {
				JavaObject refObj = (JavaObject) obj;
				try {
					System.out.println("\t\t" + refObj.getJavaClass().getName()+ " @ "+
							pointerToHexString(refObj.getID())+",");
				} catch (CorruptDataException e) {
					System.err.println("\t\t CorruptDataException while printing out array element");
					e.printStackTrace();
				}
			} else {
				System.out.println("\t\t"+obj+",");
			}
		}
		System.out.println("\t};");
	}
	
	/**
	 * Takes ImagePointer and returns it as a hex string. Perhaps this should be defined
	 * behaviour for ImagePointer.toString().
	 * @param pointer ImagePointer
	 * @return Address of pointer as a hex string prefixed with "0x"
	 */
	public static String pointerToHexString(ImagePointer pointer) {
		return "0x"+Long.toHexString(pointer.getAddress());
	}
}