Path: blob/master/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapGXLWriter.java
41161 views
/*1* Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324package sun.jvm.hotspot.utilities;2526import java.io.*;27import java.util.*;28import sun.jvm.hotspot.oops.*;29import sun.jvm.hotspot.runtime.*;3031/**32* <p>This class writes Java heap in Graph eXchange Language (GXL)33* format. GXL is an open standard for serializing arbitrary graphs in34* XML syntax.</p>35*36* <p>A GXL document contains one or more graphs. A graph contains37* nodes and edges. Both nodes and edges can have attributes. graphs,38* nodes, edges and attributes are represented by XML elements graph,39* node, edge and attr respectively. Attributes can be typed. GXL40* supports locator, bool, int, float, bool, string, enum as well as41* set, seq, bag, tup types. Nodes must have a XML attribute 'id' that42* is unique id of the node in the GXL document. Edges must have43* 'from' and 'to' XML attributes that are ids of from and to nodes.</p>44*45* <p>Java heap to GXL document mapping:</p>46* <ul>47* <li>Java object - GXL node.48* <li>Java primitive field - GXL attribute (type mapping below).49* <li>Java reference field - GXL edge from referee to referent node.50* <li>Java primitive array - GXL node with seq type attribute.51* <li>Java char array - GXL node with one attribute of string type.52* <li>Java object array - GXL node and 'length' edges.53* </ul>54*55* <p>Java primitive to GXL type mapping:</p>56* <ul>57* <li>Java byte, int, short, long - GXL int attribute58* <li>Java float, double - GXL float attribute59* <li>Java boolean - GXL bool atttribute60* <li>Java char - GXL string attribute61* </ul>62*63* Exact Java primitive type code is written in 'kind' attribute of64* 'attr' element. Type code is specified in JVM spec. second edition65* section 4.3.2 (Field Descriptor).66*67* @see <a href="http://www.gupro.de/GXL/">GXL</a>68* @see <a href="http://www.gupro.de/GXL/dtd/dtd.html">GXL DTD</a>69*/7071public class HeapGXLWriter extends AbstractHeapGraphWriter {72public void write(String fileName) throws IOException {73out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));74super.write();75if (out.checkError()) {76throw new IOException();77}78out.flush();79}8081protected void writeHeapHeader() throws IOException {82// XML processing instruction83out.print("<?xml version='1.0' encoding='");84out.print(ENCODING);85out.println("'?>");8687out.println("<gxl>");88out.println("<graph id='JavaHeap'>");8990// document properties91writeAttribute("creation-date", "string", new Date().toString());9293// write VM info94writeVMInfo();9596// emit a node for null97out.print("<node id='");98out.print(getID(null));99out.println("'/>");100}101102protected void writeObjectHeader(Oop oop) throws IOException {103refFields = new ArrayList<>();104isArray = oop.isArray();105106// generate an edge for instanceof relation107// between object node and it's class node.108writeEdge(oop, oop.getKlass().getJavaMirror(), "instanceof");109110out.print("<node id='");111out.print(getID(oop));112out.println("'>");113}114115protected void writeObjectFooter(Oop oop) throws IOException {116out.println("</node>");117118// write the reference fields as edges119for (Iterator itr = refFields.iterator(); itr.hasNext();) {120OopField field = (OopField) itr.next();121Oop ref = field.getValue(oop);122123String name = field.getID().getName();124if (isArray) {125// for arrays elements we use element<index> pattern126name = "element" + name;127} else {128name = identifierToXMLName(name);129}130writeEdge(oop, ref, name);131}132refFields = null;133}134135protected void writeObjectArray(ObjArray array) throws IOException {136writeObjectHeader(array);137writeArrayLength(array);138writeObjectFields(array);139writeObjectFooter(array);140}141142protected void writePrimitiveArray(TypeArray array)143throws IOException {144writeObjectHeader(array);145// write array length146writeArrayLength(array);147// write array elements148out.println("\t<attr name='elements'>");149TypeArrayKlass klass = (TypeArrayKlass) array.getKlass();150if (klass.getElementType() == TypeArrayKlass.T_CHAR) {151// char[] special treatment -- write it as string152out.print("\t<string>");153out.print(escapeXMLChars(OopUtilities.charArrayToString(array)));154out.println("</string>");155} else {156out.println("\t<seq>");157writeObjectFields(array);158out.println("\t</seq>");159}160out.println("\t</attr>");161writeObjectFooter(array);162}163164protected void writeClass(Instance instance) throws IOException {165writeObjectHeader(instance);166Klass reflectedType = java_lang_Class.asKlass(instance);167boolean isInstanceKlass = (reflectedType instanceof InstanceKlass);168// reflectedType is null for primitive types (int.class etc).169if (reflectedType != null) {170Symbol name = reflectedType.getName();171if (name != null) {172// write class name as an attribute173writeAttribute("class-name", "string", name.asString());174}175if (isInstanceKlass) {176// write object-size as an attribute177long sizeInBytes = reflectedType.getLayoutHelper();178writeAttribute("object-size", "int",179Long.toString(sizeInBytes));180// write static fields of this class.181writeObjectFields((InstanceKlass)reflectedType);182}183}184out.println("</node>");185186// write edges for super class and direct interfaces187if (reflectedType != null) {188Klass superType = reflectedType.getSuper();189Oop superMirror = (superType == null)?190null : superType.getJavaMirror();191writeEdge(instance, superMirror, "extends");192if (isInstanceKlass) {193// write edges for directly implemented interfaces194InstanceKlass ik = (InstanceKlass) reflectedType;195KlassArray interfaces = ik.getLocalInterfaces();196final int len = interfaces.length();197for (int i = 0; i < len; i++) {198Klass k = interfaces.getAt(i);199writeEdge(instance, k.getJavaMirror(), "implements");200}201202// write loader203Oop loader = ik.getClassLoader();204writeEdge(instance, loader, "loaded-by");205206// write signers NYI207// Oop signers = ik.getJavaMirror().getSigners();208writeEdge(instance, null, "signed-by");209210// write protection domain NYI211// Oop protectionDomain = ik.getJavaMirror().getProtectionDomain();212writeEdge(instance, null, "protection-domain");213214// write edges for static reference fields from this class215for (Iterator itr = refFields.iterator(); itr.hasNext();) {216OopField field = (OopField) itr.next();217Oop ref = field.getValue(reflectedType);218String name = field.getID().getName();219writeEdge(instance, ref, identifierToXMLName(name));220}221}222}223refFields = null;224}225226protected void writeReferenceField(Oop oop, OopField field)227throws IOException {228refFields.add(field);229}230231protected void writeByteField(Oop oop, ByteField field)232throws IOException {233writeField(field, "int", "B", Byte.toString(field.getValue(oop)));234}235236protected void writeCharField(Oop oop, CharField field)237throws IOException {238writeField(field, "string", "C",239escapeXMLChars(Character.toString(field.getValue(oop))));240}241242protected void writeBooleanField(Oop oop, BooleanField field)243throws IOException {244writeField(field, "bool", "Z", Boolean.toString(field.getValue(oop)));245}246247protected void writeShortField(Oop oop, ShortField field)248throws IOException {249writeField(field, "int", "S", Short.toString(field.getValue(oop)));250}251252protected void writeIntField(Oop oop, IntField field)253throws IOException {254writeField(field, "int", "I", Integer.toString(field.getValue(oop)));255}256257protected void writeLongField(Oop oop, LongField field)258throws IOException {259writeField(field, "int", "J", Long.toString(field.getValue(oop)));260}261262protected void writeFloatField(Oop oop, FloatField field)263throws IOException {264writeField(field, "float", "F", Float.toString(field.getValue(oop)));265}266267protected void writeDoubleField(Oop oop, DoubleField field)268throws IOException {269writeField(field, "float", "D", Double.toString(field.getValue(oop)));270}271272protected void writeHeapFooter() throws IOException {273out.println("</graph>");274out.println("</gxl>");275}276277//-- Internals only below this point278279// Java identifier to XML NMTOKEN type string280private static String identifierToXMLName(String name) {281// for now, just replace '$' with '_'282return name.replace('$', '_');283}284285// escapes XML meta-characters and illegal characters286private static String escapeXMLChars(String s) {287// FIXME: is there a better way or API?288StringBuilder result = null;289for(int i = 0, max = s.length(), delta = 0; i < max; i++) {290char c = s.charAt(i);291String replacement = null;292if (c == '&') {293replacement = "&";294} else if (c == '<') {295replacement = "<";296} else if (c == '>') {297replacement = ">";298} else if (c == '"') {299replacement = """;300} else if (c == '\'') {301replacement = "'";302} else if (c < '\u0020' || (c > '\ud7ff' && c < '\ue000') ||303c == '\ufffe' || c == '\uffff') {304// These are illegal in XML -- put these in a CDATA section.305// Refer to section 2.2 Characters in XML specification at306// http://www.w3.org/TR/2004/REC-xml-20040204/307replacement = "<![CDATA[&#x" +308Integer.toHexString((int)c) + ";]]>";309}310311if (replacement != null) {312if (result == null) {313result = new StringBuilder(s);314}315result.replace(i + delta, i + delta + 1, replacement);316delta += (replacement.length() - 1);317}318}319if (result == null) {320return s;321}322return result.toString();323}324325private static String getID(Oop oop) {326// address as unique id for node -- prefixed by "ID_".327if (oop == null) {328return "ID_NULL";329} else {330return "ID_" + oop.getHandle().toString();331}332}333334private void writeArrayLength(Array array) throws IOException {335writeAttribute("length", "int",336Integer.toString((int) array.getLength()));337}338339private void writeAttribute(String name, String type, String value) {340out.print("\t<attr name='");341out.print(name);342out.print("'><");343out.print(type);344out.print('>');345out.print(value);346out.print("</");347out.print(type);348out.println("></attr>");349}350351private void writeEdge(Oop from, Oop to, String name) throws IOException {352out.print("<edge from='");353out.print(getID(from));354out.print("' to='");355out.print(getID(to));356out.println("'>");357writeAttribute("name", "string", name);358out.println("</edge>");359}360361private void writeField(Field field, String type, String kind,362String value) throws IOException {363// 'type' is GXL type of the attribute364// 'kind' is Java type code ("B", "C", "Z", "S", "I", "J", "F", "D")365if (isArray) {366out.print('\t');367} else {368out.print("\t<attr name='");369String name = field.getID().getName();370out.print(identifierToXMLName(name));371out.print("' kind='");372out.print(kind);373out.print("'>");374}375out.print('<');376out.print(type);377out.print('>');378out.print(value);379out.print("</");380out.print(type);381out.print('>');382if (isArray) {383out.println();384} else {385out.println("</attr>");386}387}388389private void writeVMInfo() throws IOException {390VM vm = VM.getVM();391writeAttribute("vm-version", "string", vm.getVMRelease());392writeAttribute("vm-type", "string",393(vm.isClientCompiler())? "client" :394((vm.isServerCompiler())? "server" : "core"));395writeAttribute("os", "string", vm.getOS());396writeAttribute("cpu", "string", vm.getCPU());397writeAttribute("pointer-size", "string",398Integer.toString((int)vm.getOopSize() * 8));399}400401// XML encoding that we'll use402private static final String ENCODING = "UTF-8";403404// reference fields of currently visited object405private List<OopField> refFields;406// are we writing an array now?407private boolean isArray;408private PrintWriter out;409}410411412