/* * Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ package sun.jvm.hotspot.utilities; import java.io.*; import java.util.*; import sun.jvm.hotspot.oops.*; import sun.jvm.hotspot.runtime.*; /** *

This class writes Java heap in Graph eXchange Language (GXL) * format. GXL is an open standard for serializing arbitrary graphs in * XML syntax.

* *

A GXL document contains one or more graphs. A graph contains * nodes and edges. Both nodes and edges can have attributes. graphs, * nodes, edges and attributes are represented by XML elements graph, * node, edge and attr respectively. Attributes can be typed. GXL * supports locator, bool, int, float, bool, string, enum as well as * set, seq, bag, tup types. Nodes must have a XML attribute 'id' that * is unique id of the node in the GXL document. Edges must have * 'from' and 'to' XML attributes that are ids of from and to nodes.

* *

Java heap to GXL document mapping:

* * *

Java primitive to GXL type mapping:

* * * Exact Java primitive type code is written in 'kind' attribute of * 'attr' element. Type code is specified in JVM spec. second edition * section 4.3.2 (Field Descriptor). * * @see GXL * @see GXL DTD */ public class HeapGXLWriter extends AbstractHeapGraphWriter { public void write(String fileName) throws IOException { out = new PrintWriter(new BufferedWriter(new FileWriter(fileName))); super.write(); if (out.checkError()) { throw new IOException(); } out.flush(); } protected void writeHeapHeader() throws IOException { // XML processing instruction out.print(""); out.println(""); out.println(""); // document properties writeAttribute("creation-date", "string", new Date().toString()); // write VM info writeVMInfo(); // emit a node for null out.print(""); } protected void writeObjectHeader(Oop oop) throws IOException { refFields = new ArrayList(); isArray = oop.isArray(); // generate an edge for instanceof relation // between object node and it's class node. writeEdge(oop, oop.getKlass().getJavaMirror(), "instanceof"); out.print(""); } protected void writeObjectFooter(Oop oop) throws IOException { out.println(""); // write the reference fields as edges for (Iterator itr = refFields.iterator(); itr.hasNext();) { OopField field = (OopField) itr.next(); Oop ref = field.getValue(oop); String name = field.getID().getName(); if (isArray) { // for arrays elements we use element pattern name = "element" + name; } else { name = identifierToXMLName(name); } writeEdge(oop, ref, name); } refFields = null; } protected void writeObjectArray(ObjArray array) throws IOException { writeObjectHeader(array); writeArrayLength(array); writeObjectFields(array); writeObjectFooter(array); } protected void writePrimitiveArray(TypeArray array) throws IOException { writeObjectHeader(array); // write array length writeArrayLength(array); // write array elements out.println("\t"); TypeArrayKlass klass = (TypeArrayKlass) array.getKlass(); if (klass.getElementType() == TypeArrayKlass.T_CHAR) { // char[] special treatment -- write it as string out.print("\t"); out.print(escapeXMLChars(OopUtilities.charArrayToString(array))); out.println(""); } else { out.println("\t"); writeObjectFields(array); out.println("\t"); } out.println("\t"); writeObjectFooter(array); } protected void writeClass(Instance instance) throws IOException { writeObjectHeader(instance); Klass reflectedType = java_lang_Class.asKlass(instance); boolean isInstanceKlass = (reflectedType instanceof InstanceKlass); // reflectedType is null for primitive types (int.class etc). if (reflectedType != null) { Symbol name = reflectedType.getName(); if (name != null) { // write class name as an attribute writeAttribute("class-name", "string", name.asString()); } if (isInstanceKlass) { // write object-size as an attribute long sizeInBytes = reflectedType.getLayoutHelper(); writeAttribute("object-size", "int", Long.toString(sizeInBytes)); // write static fields of this class. writeObjectFields(reflectedType); } } out.println(""); // write edges for super class and direct interfaces if (reflectedType != null) { Klass superType = reflectedType.getSuper(); Oop superMirror = (superType == null)? null : superType.getJavaMirror(); writeEdge(instance, superMirror, "extends"); if (isInstanceKlass) { // write edges for directly implemented interfaces InstanceKlass ik = (InstanceKlass) reflectedType; ObjArray interfaces = ik.getLocalInterfaces(); final int len = (int) interfaces.getLength(); for (int i = 0; i < len; i++) { Klass k = (Klass) interfaces.getObjAt(i); writeEdge(instance, k.getJavaMirror(), "implements"); } // write loader Oop loader = ik.getClassLoader(); writeEdge(instance, loader, "loaded-by"); // write signers Oop signers = ik.getSigners(); writeEdge(instance, signers, "signed-by"); // write protection domain Oop protectionDomain = ik.getProtectionDomain(); writeEdge(instance, protectionDomain, "protection-domain"); // write edges for static reference fields from this class for (Iterator itr = refFields.iterator(); itr.hasNext();) { OopField field = (OopField) itr.next(); Oop ref = field.getValue(reflectedType); String name = field.getID().getName(); writeEdge(instance, ref, identifierToXMLName(name)); } } } refFields = null; } protected void writeReferenceField(Oop oop, OopField field) throws IOException { refFields.add(field); } protected void writeByteField(Oop oop, ByteField field) throws IOException { writeField(field, "int", "B", Byte.toString(field.getValue(oop))); } protected void writeCharField(Oop oop, CharField field) throws IOException { writeField(field, "string", "C", escapeXMLChars(Character.toString(field.getValue(oop)))); } protected void writeBooleanField(Oop oop, BooleanField field) throws IOException { writeField(field, "bool", "Z", Boolean.toString(field.getValue(oop))); } protected void writeShortField(Oop oop, ShortField field) throws IOException { writeField(field, "int", "S", Short.toString(field.getValue(oop))); } protected void writeIntField(Oop oop, IntField field) throws IOException { writeField(field, "int", "I", Integer.toString(field.getValue(oop))); } protected void writeLongField(Oop oop, LongField field) throws IOException { writeField(field, "int", "J", Long.toString(field.getValue(oop))); } protected void writeFloatField(Oop oop, FloatField field) throws IOException { writeField(field, "float", "F", Float.toString(field.getValue(oop))); } protected void writeDoubleField(Oop oop, DoubleField field) throws IOException { writeField(field, "float", "D", Double.toString(field.getValue(oop))); } protected void writeHeapFooter() throws IOException { out.println(""); out.println(""); } //-- Internals only below this point // Java identifier to XML NMTOKEN type string private static String identifierToXMLName(String name) { // for now, just replace '$' with '_' return name.replace('$', '_'); } // escapes XML meta-characters and illegal characters private static String escapeXMLChars(String s) { // FIXME: is there a better way or API? StringBuffer result = null; for(int i = 0, max = s.length(), delta = 0; i < max; i++) { char c = s.charAt(i); String replacement = null; if (c == '&') { replacement = "&"; } else if (c == '<') { replacement = "<"; } else if (c == '>') { replacement = ">"; } else if (c == '"') { replacement = """; } else if (c == '\'') { replacement = "'"; } else if (c < '\u0020' || (c > '\ud7ff' && c < '\ue000') || c == '\ufffe' || c == '\uffff') { // These are illegal in XML -- put these in a CDATA section. // Refer to section 2.2 Characters in XML specification at // http://www.w3.org/TR/2004/REC-xml-20040204/ replacement = ""; } if (replacement != null) { if (result == null) { result = new StringBuffer(s); } result.replace(i + delta, i + delta + 1, replacement); delta += (replacement.length() - 1); } } if (result == null) { return s; } return result.toString(); } private static String getID(Oop oop) { // address as unique id for node -- prefixed by "ID_". if (oop == null) { return "ID_NULL"; } else { return "ID_" + oop.getHandle().toString(); } } private void writeArrayLength(Array array) throws IOException { writeAttribute("length", "int", Integer.toString((int) array.getLength())); } private void writeAttribute(String name, String type, String value) { out.print("\t<"); out.print(type); out.print('>'); out.print(value); out.print(""); } private void writeEdge(Oop from, Oop to, String name) throws IOException { out.print(""); writeAttribute("name", "string", name); out.println(""); } private void writeField(Field field, String type, String kind, String value) throws IOException { // 'type' is GXL type of the attribute // 'kind' is Java type code ("B", "C", "Z", "S", "I", "J", "F", "D") if (isArray) { out.print('\t'); } else { out.print("\t"); } out.print('<'); out.print(type); out.print('>'); out.print(value); out.print("'); if (isArray) { out.println(); } else { out.println(""); } } private void writeVMInfo() throws IOException { VM vm = VM.getVM(); writeAttribute("vm-version", "string", vm.getVMRelease()); writeAttribute("vm-type", "string", (vm.isClientCompiler())? "client" : ((vm.isServerCompiler())? "server" : "core")); writeAttribute("os", "string", vm.getOS()); writeAttribute("cpu", "string", vm.getCPU()); writeAttribute("pointer-size", "string", Integer.toString((int)vm.getOopSize() * 8)); } // XML encoding that we'll use private static final String ENCODING = "UTF-8"; // reference fields of currently visited object private List/**/ refFields; // are we writing an array now? private boolean isArray; private PrintWriter out; }