view agent/src/share/classes/sun/jvm/hotspot/utilities/HeapGXLWriter.java @ 17716:cdb71841f4bc

6498581: ThreadInterruptTest3 produces wrong output on Windows Summary: There is race condition between os::interrupt and os::is_interrupted on Windows. In JVM_Sleep(Thread.sleep), check if thread gets interrupted, it may see interrupted but not really interrupted so cause spurious waking up (early return from sleep). Fix by checking if interrupt event really gets set thus prevent false return. For intrinsic of _isInterrupted, on Windows, go fastpath only on bit not set. Reviewed-by: acorn, kvn Contributed-by: david.holmes@oracle.com, yumin.qi@oracle.com
author minqi
date Wed, 26 Feb 2014 15:20:41 -0800
parents 55fb97c4c58d
children 4ca6dc0799b6
line wrap: on
line source

/*
 * Copyright (c) 2004, 2013, 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.*;

/**
 * <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 = 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((InstanceKlass)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;
                KlassArray interfaces = ik.getLocalInterfaces();
                final int len = interfaces.length();
                for (int i = 0; i < len; i++) {
                    Klass k = interfaces.getAt(i);
                    writeEdge(instance, k.getJavaMirror(), "implements");
                }

                // write loader
                Oop loader = ik.getClassLoader();
                writeEdge(instance, loader, "loaded-by");

                // write signers NYI
                // Oop signers = ik.getJavaMirror().getSigners();
                writeEdge(instance, null, "signed-by");

                // write protection domain NYI
                // Oop protectionDomain = ik.getJavaMirror().getProtectionDomain();
                writeEdge(instance, null, "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 = "&amp;";
            } else if (c == '<') {
                replacement = "&lt;";
            } else if (c == '>') {
                replacement = "&gt;";
            } else if (c == '"') {
                replacement = "&quot;";
            } else if (c == '\'') {
                replacement = "&apos;";
            } 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;
}