--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapGXLWriter.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2004-2007 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.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.*;
+
+/**
+ * <p>This class writes Java heap in Graph eXchange Language (GXL)
+ * format. GXL is an open standard for serializing arbitrary graphs in
+ * XML syntax.</p>
+ *
+ * <p>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.</p>
+ *
+ * <p>Java heap to GXL document mapping:</p>
+ * <ul>
+ * <li>Java object - GXL node.
+ * <li>Java primitive field - GXL attribute (type mapping below).
+ * <li>Java reference field - GXL edge from referee to referent node.
+ * <li>Java primitive array - GXL node with seq type attribute.
+ * <li>Java char array - GXL node with one attribute of string type.
+ * <li>Java object array - GXL node and 'length' edges.
+ * </ul>
+ *
+ * <p>Java primitive to GXL type mapping:</p>
+ * <ul>
+ * <li>Java byte, int, short, long - GXL int attribute
+ * <li>Java float, double - GXL float attribute
+ * <li>Java boolean - GXL bool atttribute
+ * <li>Java char - GXL string attribute
+ * </ul>
+ *
+ * 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 <a href="http://www.gupro.de/GXL/">GXL</a>
+ * @see <a href="http://www.gupro.de/GXL/dtd/dtd.html">GXL DTD</a>
+ */
+
+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("<?xml version='1.0' encoding='");
+ out.print(ENCODING);
+ out.println("'?>");
+
+ out.println("<gxl>");
+ out.println("<graph id='JavaHeap'>");
+
+ // document properties
+ writeAttribute("creation-date", "string", new Date().toString());
+
+ // write VM info
+ writeVMInfo();
+
+ // emit a node for null
+ out.print("<node id='");
+ out.print(getID(null));
+ out.println("'/>");
+ }
+
+ 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("<node id='");
+ out.print(getID(oop));
+ out.println("'>");
+ }
+
+ protected void writeObjectFooter(Oop oop) throws IOException {
+ out.println("</node>");
+
+ // 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<index> 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<attr name='elements'>");
+ TypeArrayKlass klass = (TypeArrayKlass) array.getKlass();
+ if (klass.getElementType() == TypeArrayKlass.T_CHAR) {
+ // char[] special treatment -- write it as string
+ out.print("\t<string>");
+ out.print(escapeXMLChars(OopUtilities.charArrayToString(array)));
+ out.println("</string>");
+ } else {
+ out.println("\t<seq>");
+ writeObjectFields(array);
+ out.println("\t</seq>");
+ }
+ out.println("\t</attr>");
+ writeObjectFooter(array);
+ }
+
+ protected void writeClass(Instance instance) throws IOException {
+ writeObjectHeader(instance);
+ Klass reflectedType = OopUtilities.classOopToKlass(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("</node>");
+
+ // 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("</graph>");
+ out.println("</gxl>");
+ }
+
+ //-- 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 = "<![CDATA[&#x" +
+ Integer.toHexString((int)c) + ";]]>";
+ }
+
+ 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<attr name='");
+ out.print(name);
+ out.print("'><");
+ out.print(type);
+ out.print('>');
+ out.print(value);
+ out.print("</");
+ out.print(type);
+ out.println("></attr>");
+ }
+
+ private void writeEdge(Oop from, Oop to, String name) throws IOException {
+ out.print("<edge from='");
+ out.print(getID(from));
+ out.print("' to='");
+ out.print(getID(to));
+ out.println("'>");
+ writeAttribute("name", "string", name);
+ out.println("</edge>");
+ }
+
+ 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<attr name='");
+ String name = field.getID().getName();
+ out.print(identifierToXMLName(name));
+ out.print("' kind='");
+ out.print(kind);
+ out.print("'>");
+ }
+ out.print('<');
+ out.print(type);
+ out.print('>');
+ out.print(value);
+ out.print("</");
+ out.print(type);
+ out.print('>');
+ if (isArray) {
+ out.println();
+ } else {
+ out.println("</attr>");
+ }
+ }
+
+ 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/*<OopField>*/ refFields;
+ // are we writing an array now?
+ private boolean isArray;
+ private PrintWriter out;
+}