| Kato > Index > 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.
/******************************************************************************* * 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()); } }