0
|
1 /*
|
|
2 * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved.
|
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
4 *
|
|
5 * This code is free software; you can redistribute it and/or modify it
|
|
6 * under the terms of the GNU General Public License version 2 only, as
|
|
7 * published by the Free Software Foundation.
|
|
8 *
|
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT
|
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
12 * version 2 for more details (a copy is included in the LICENSE file that
|
|
13 * accompanied this code).
|
|
14 *
|
|
15 * You should have received a copy of the GNU General Public License version
|
|
16 * 2 along with this work; if not, write to the Free Software Foundation,
|
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
18 *
|
|
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
|
20 * CA 95054 USA or visit www.sun.com if you need additional information or
|
|
21 * have any questions.
|
|
22 *
|
|
23 */
|
|
24
|
|
25 package sun.jvm.hotspot.utilities;
|
|
26
|
|
27 import java.io.*;
|
|
28 import java.util.*;
|
|
29 import sun.jvm.hotspot.oops.*;
|
|
30 import sun.jvm.hotspot.runtime.*;
|
|
31
|
|
32 /**
|
|
33 * <p>This class writes Java heap in Graph eXchange Language (GXL)
|
|
34 * format. GXL is an open standard for serializing arbitrary graphs in
|
|
35 * XML syntax.</p>
|
|
36 *
|
|
37 * <p>A GXL document contains one or more graphs. A graph contains
|
|
38 * nodes and edges. Both nodes and edges can have attributes. graphs,
|
|
39 * nodes, edges and attributes are represented by XML elements graph,
|
|
40 * node, edge and attr respectively. Attributes can be typed. GXL
|
|
41 * supports locator, bool, int, float, bool, string, enum as well as
|
|
42 * set, seq, bag, tup types. Nodes must have a XML attribute 'id' that
|
|
43 * is unique id of the node in the GXL document. Edges must have
|
|
44 * 'from' and 'to' XML attributes that are ids of from and to nodes.</p>
|
|
45 *
|
|
46 * <p>Java heap to GXL document mapping:</p>
|
|
47 * <ul>
|
|
48 * <li>Java object - GXL node.
|
|
49 * <li>Java primitive field - GXL attribute (type mapping below).
|
|
50 * <li>Java reference field - GXL edge from referee to referent node.
|
|
51 * <li>Java primitive array - GXL node with seq type attribute.
|
|
52 * <li>Java char array - GXL node with one attribute of string type.
|
|
53 * <li>Java object array - GXL node and 'length' edges.
|
|
54 * </ul>
|
|
55 *
|
|
56 * <p>Java primitive to GXL type mapping:</p>
|
|
57 * <ul>
|
|
58 * <li>Java byte, int, short, long - GXL int attribute
|
|
59 * <li>Java float, double - GXL float attribute
|
|
60 * <li>Java boolean - GXL bool atttribute
|
|
61 * <li>Java char - GXL string attribute
|
|
62 * </ul>
|
|
63 *
|
|
64 * Exact Java primitive type code is written in 'kind' attribute of
|
|
65 * 'attr' element. Type code is specified in JVM spec. second edition
|
|
66 * section 4.3.2 (Field Descriptor).
|
|
67 *
|
|
68 * @see <a href="http://www.gupro.de/GXL/">GXL</a>
|
|
69 * @see <a href="http://www.gupro.de/GXL/dtd/dtd.html">GXL DTD</a>
|
|
70 */
|
|
71
|
|
72 public class HeapGXLWriter extends AbstractHeapGraphWriter {
|
|
73 public void write(String fileName) throws IOException {
|
|
74 out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
|
|
75 super.write();
|
|
76 if (out.checkError()) {
|
|
77 throw new IOException();
|
|
78 }
|
|
79 out.flush();
|
|
80 }
|
|
81
|
|
82 protected void writeHeapHeader() throws IOException {
|
|
83 // XML processing instruction
|
|
84 out.print("<?xml version='1.0' encoding='");
|
|
85 out.print(ENCODING);
|
|
86 out.println("'?>");
|
|
87
|
|
88 out.println("<gxl>");
|
|
89 out.println("<graph id='JavaHeap'>");
|
|
90
|
|
91 // document properties
|
|
92 writeAttribute("creation-date", "string", new Date().toString());
|
|
93
|
|
94 // write VM info
|
|
95 writeVMInfo();
|
|
96
|
|
97 // emit a node for null
|
|
98 out.print("<node id='");
|
|
99 out.print(getID(null));
|
|
100 out.println("'/>");
|
|
101 }
|
|
102
|
|
103 protected void writeObjectHeader(Oop oop) throws IOException {
|
|
104 refFields = new ArrayList();
|
|
105 isArray = oop.isArray();
|
|
106
|
|
107 // generate an edge for instanceof relation
|
|
108 // between object node and it's class node.
|
|
109 writeEdge(oop, oop.getKlass().getJavaMirror(), "instanceof");
|
|
110
|
|
111 out.print("<node id='");
|
|
112 out.print(getID(oop));
|
|
113 out.println("'>");
|
|
114 }
|
|
115
|
|
116 protected void writeObjectFooter(Oop oop) throws IOException {
|
|
117 out.println("</node>");
|
|
118
|
|
119 // write the reference fields as edges
|
|
120 for (Iterator itr = refFields.iterator(); itr.hasNext();) {
|
|
121 OopField field = (OopField) itr.next();
|
|
122 Oop ref = field.getValue(oop);
|
|
123
|
|
124 String name = field.getID().getName();
|
|
125 if (isArray) {
|
|
126 // for arrays elements we use element<index> pattern
|
|
127 name = "element" + name;
|
|
128 } else {
|
|
129 name = identifierToXMLName(name);
|
|
130 }
|
|
131 writeEdge(oop, ref, name);
|
|
132 }
|
|
133 refFields = null;
|
|
134 }
|
|
135
|
|
136 protected void writeObjectArray(ObjArray array) throws IOException {
|
|
137 writeObjectHeader(array);
|
|
138 writeArrayLength(array);
|
|
139 writeObjectFields(array);
|
|
140 writeObjectFooter(array);
|
|
141 }
|
|
142
|
|
143 protected void writePrimitiveArray(TypeArray array)
|
|
144 throws IOException {
|
|
145 writeObjectHeader(array);
|
|
146 // write array length
|
|
147 writeArrayLength(array);
|
|
148 // write array elements
|
|
149 out.println("\t<attr name='elements'>");
|
|
150 TypeArrayKlass klass = (TypeArrayKlass) array.getKlass();
|
|
151 if (klass.getElementType() == TypeArrayKlass.T_CHAR) {
|
|
152 // char[] special treatment -- write it as string
|
|
153 out.print("\t<string>");
|
|
154 out.print(escapeXMLChars(OopUtilities.charArrayToString(array)));
|
|
155 out.println("</string>");
|
|
156 } else {
|
|
157 out.println("\t<seq>");
|
|
158 writeObjectFields(array);
|
|
159 out.println("\t</seq>");
|
|
160 }
|
|
161 out.println("\t</attr>");
|
|
162 writeObjectFooter(array);
|
|
163 }
|
|
164
|
|
165 protected void writeClass(Instance instance) throws IOException {
|
|
166 writeObjectHeader(instance);
|
|
167 Klass reflectedType = OopUtilities.classOopToKlass(instance);
|
|
168 boolean isInstanceKlass = (reflectedType instanceof InstanceKlass);
|
|
169 // reflectedType is null for primitive types (int.class etc).
|
|
170 if (reflectedType != null) {
|
|
171 Symbol name = reflectedType.getName();
|
|
172 if (name != null) {
|
|
173 // write class name as an attribute
|
|
174 writeAttribute("class-name", "string", name.asString());
|
|
175 }
|
|
176 if (isInstanceKlass) {
|
|
177 // write object-size as an attribute
|
|
178 long sizeInBytes = reflectedType.getLayoutHelper();
|
|
179 writeAttribute("object-size", "int",
|
|
180 Long.toString(sizeInBytes));
|
|
181 // write static fields of this class.
|
|
182 writeObjectFields(reflectedType);
|
|
183 }
|
|
184 }
|
|
185 out.println("</node>");
|
|
186
|
|
187 // write edges for super class and direct interfaces
|
|
188 if (reflectedType != null) {
|
|
189 Klass superType = reflectedType.getSuper();
|
|
190 Oop superMirror = (superType == null)?
|
|
191 null : superType.getJavaMirror();
|
|
192 writeEdge(instance, superMirror, "extends");
|
|
193 if (isInstanceKlass) {
|
|
194 // write edges for directly implemented interfaces
|
|
195 InstanceKlass ik = (InstanceKlass) reflectedType;
|
|
196 ObjArray interfaces = ik.getLocalInterfaces();
|
|
197 final int len = (int) interfaces.getLength();
|
|
198 for (int i = 0; i < len; i++) {
|
|
199 Klass k = (Klass) interfaces.getObjAt(i);
|
|
200 writeEdge(instance, k.getJavaMirror(), "implements");
|
|
201 }
|
|
202
|
|
203 // write loader
|
|
204 Oop loader = ik.getClassLoader();
|
|
205 writeEdge(instance, loader, "loaded-by");
|
|
206
|
|
207 // write signers
|
|
208 Oop signers = ik.getSigners();
|
|
209 writeEdge(instance, signers, "signed-by");
|
|
210
|
|
211 // write protection domain
|
|
212 Oop protectionDomain = ik.getProtectionDomain();
|
|
213 writeEdge(instance, protectionDomain, "protection-domain");
|
|
214
|
|
215 // write edges for static reference fields from this class
|
|
216 for (Iterator itr = refFields.iterator(); itr.hasNext();) {
|
|
217 OopField field = (OopField) itr.next();
|
|
218 Oop ref = field.getValue(reflectedType);
|
|
219 String name = field.getID().getName();
|
|
220 writeEdge(instance, ref, identifierToXMLName(name));
|
|
221 }
|
|
222 }
|
|
223 }
|
|
224 refFields = null;
|
|
225 }
|
|
226
|
|
227 protected void writeReferenceField(Oop oop, OopField field)
|
|
228 throws IOException {
|
|
229 refFields.add(field);
|
|
230 }
|
|
231
|
|
232 protected void writeByteField(Oop oop, ByteField field)
|
|
233 throws IOException {
|
|
234 writeField(field, "int", "B", Byte.toString(field.getValue(oop)));
|
|
235 }
|
|
236
|
|
237 protected void writeCharField(Oop oop, CharField field)
|
|
238 throws IOException {
|
|
239 writeField(field, "string", "C",
|
|
240 escapeXMLChars(Character.toString(field.getValue(oop))));
|
|
241 }
|
|
242
|
|
243 protected void writeBooleanField(Oop oop, BooleanField field)
|
|
244 throws IOException {
|
|
245 writeField(field, "bool", "Z", Boolean.toString(field.getValue(oop)));
|
|
246 }
|
|
247
|
|
248 protected void writeShortField(Oop oop, ShortField field)
|
|
249 throws IOException {
|
|
250 writeField(field, "int", "S", Short.toString(field.getValue(oop)));
|
|
251 }
|
|
252
|
|
253 protected void writeIntField(Oop oop, IntField field)
|
|
254 throws IOException {
|
|
255 writeField(field, "int", "I", Integer.toString(field.getValue(oop)));
|
|
256 }
|
|
257
|
|
258 protected void writeLongField(Oop oop, LongField field)
|
|
259 throws IOException {
|
|
260 writeField(field, "int", "J", Long.toString(field.getValue(oop)));
|
|
261 }
|
|
262
|
|
263 protected void writeFloatField(Oop oop, FloatField field)
|
|
264 throws IOException {
|
|
265 writeField(field, "float", "F", Float.toString(field.getValue(oop)));
|
|
266 }
|
|
267
|
|
268 protected void writeDoubleField(Oop oop, DoubleField field)
|
|
269 throws IOException {
|
|
270 writeField(field, "float", "D", Double.toString(field.getValue(oop)));
|
|
271 }
|
|
272
|
|
273 protected void writeHeapFooter() throws IOException {
|
|
274 out.println("</graph>");
|
|
275 out.println("</gxl>");
|
|
276 }
|
|
277
|
|
278 //-- Internals only below this point
|
|
279
|
|
280 // Java identifier to XML NMTOKEN type string
|
|
281 private static String identifierToXMLName(String name) {
|
|
282 // for now, just replace '$' with '_'
|
|
283 return name.replace('$', '_');
|
|
284 }
|
|
285
|
|
286 // escapes XML meta-characters and illegal characters
|
|
287 private static String escapeXMLChars(String s) {
|
|
288 // FIXME: is there a better way or API?
|
|
289 StringBuffer result = null;
|
|
290 for(int i = 0, max = s.length(), delta = 0; i < max; i++) {
|
|
291 char c = s.charAt(i);
|
|
292 String replacement = null;
|
|
293 if (c == '&') {
|
|
294 replacement = "&";
|
|
295 } else if (c == '<') {
|
|
296 replacement = "<";
|
|
297 } else if (c == '>') {
|
|
298 replacement = ">";
|
|
299 } else if (c == '"') {
|
|
300 replacement = """;
|
|
301 } else if (c == '\'') {
|
|
302 replacement = "'";
|
|
303 } else if (c < '\u0020' || (c > '\ud7ff' && c < '\ue000') ||
|
|
304 c == '\ufffe' || c == '\uffff') {
|
|
305 // These are illegal in XML -- put these in a CDATA section.
|
|
306 // Refer to section 2.2 Characters in XML specification at
|
|
307 // http://www.w3.org/TR/2004/REC-xml-20040204/
|
|
308 replacement = "<![CDATA[&#x" +
|
|
309 Integer.toHexString((int)c) + ";]]>";
|
|
310 }
|
|
311
|
|
312 if (replacement != null) {
|
|
313 if (result == null) {
|
|
314 result = new StringBuffer(s);
|
|
315 }
|
|
316 result.replace(i + delta, i + delta + 1, replacement);
|
|
317 delta += (replacement.length() - 1);
|
|
318 }
|
|
319 }
|
|
320 if (result == null) {
|
|
321 return s;
|
|
322 }
|
|
323 return result.toString();
|
|
324 }
|
|
325
|
|
326 private static String getID(Oop oop) {
|
|
327 // address as unique id for node -- prefixed by "ID_".
|
|
328 if (oop == null) {
|
|
329 return "ID_NULL";
|
|
330 } else {
|
|
331 return "ID_" + oop.getHandle().toString();
|
|
332 }
|
|
333 }
|
|
334
|
|
335 private void writeArrayLength(Array array) throws IOException {
|
|
336 writeAttribute("length", "int",
|
|
337 Integer.toString((int) array.getLength()));
|
|
338 }
|
|
339
|
|
340 private void writeAttribute(String name, String type, String value) {
|
|
341 out.print("\t<attr name='");
|
|
342 out.print(name);
|
|
343 out.print("'><");
|
|
344 out.print(type);
|
|
345 out.print('>');
|
|
346 out.print(value);
|
|
347 out.print("</");
|
|
348 out.print(type);
|
|
349 out.println("></attr>");
|
|
350 }
|
|
351
|
|
352 private void writeEdge(Oop from, Oop to, String name) throws IOException {
|
|
353 out.print("<edge from='");
|
|
354 out.print(getID(from));
|
|
355 out.print("' to='");
|
|
356 out.print(getID(to));
|
|
357 out.println("'>");
|
|
358 writeAttribute("name", "string", name);
|
|
359 out.println("</edge>");
|
|
360 }
|
|
361
|
|
362 private void writeField(Field field, String type, String kind,
|
|
363 String value) throws IOException {
|
|
364 // 'type' is GXL type of the attribute
|
|
365 // 'kind' is Java type code ("B", "C", "Z", "S", "I", "J", "F", "D")
|
|
366 if (isArray) {
|
|
367 out.print('\t');
|
|
368 } else {
|
|
369 out.print("\t<attr name='");
|
|
370 String name = field.getID().getName();
|
|
371 out.print(identifierToXMLName(name));
|
|
372 out.print("' kind='");
|
|
373 out.print(kind);
|
|
374 out.print("'>");
|
|
375 }
|
|
376 out.print('<');
|
|
377 out.print(type);
|
|
378 out.print('>');
|
|
379 out.print(value);
|
|
380 out.print("</");
|
|
381 out.print(type);
|
|
382 out.print('>');
|
|
383 if (isArray) {
|
|
384 out.println();
|
|
385 } else {
|
|
386 out.println("</attr>");
|
|
387 }
|
|
388 }
|
|
389
|
|
390 private void writeVMInfo() throws IOException {
|
|
391 VM vm = VM.getVM();
|
|
392 writeAttribute("vm-version", "string", vm.getVMRelease());
|
|
393 writeAttribute("vm-type", "string",
|
|
394 (vm.isClientCompiler())? "client" :
|
|
395 ((vm.isServerCompiler())? "server" : "core"));
|
|
396 writeAttribute("os", "string", vm.getOS());
|
|
397 writeAttribute("cpu", "string", vm.getCPU());
|
|
398 writeAttribute("pointer-size", "string",
|
|
399 Integer.toString((int)vm.getOopSize() * 8));
|
|
400 }
|
|
401
|
|
402 // XML encoding that we'll use
|
|
403 private static final String ENCODING = "UTF-8";
|
|
404
|
|
405 // reference fields of currently visited object
|
|
406 private List/*<OopField>*/ refFields;
|
|
407 // are we writing an array now?
|
|
408 private boolean isArray;
|
|
409 private PrintWriter out;
|
|
410 }
|