1 /*
2  * Copyright (c) 2004, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  *
23  */
24 
25 package sun.jvm.hotspot.utilities;
26 
27 import java.io.*;
28 import sun.jvm.hotspot.debugger.*;
29 import sun.jvm.hotspot.gc.shared.OopStorage;
30 import sun.jvm.hotspot.memory.*;
31 import sun.jvm.hotspot.oops.*;
32 import sun.jvm.hotspot.runtime.*;
33 
34 /**
35  * This is abstract base class for heap graph writers. This class does
36  * not assume any file format for the heap graph. It hides heap
37  * iteration, object (fields) iteration mechanism from derived
38  * classes. This class does not even accept OutputStream etc. so that
39  * derived class can construct specific writer/filter from input
40  * stream.
41  */
42 
43 public abstract class AbstractHeapGraphWriter implements HeapGraphWriter {
44     // the function iterates heap and calls Oop type specific writers
write()45     protected void write() throws IOException {
46         javaLangClass = "java/lang/Class";
47         javaLangString = "java/lang/String";
48         javaLangThread = "java/lang/Thread";
49         ObjectHeap heap = VM.getVM().getObjectHeap();
50         try {
51             heap.iterate(new DefaultHeapVisitor() {
52                     public void prologue(long usedSize) {
53                         try {
54                             writeHeapHeader();
55                         } catch (IOException exp) {
56                             throw new RuntimeException(exp);
57                         }
58                     }
59 
60                     public boolean doObj(Oop oop) {
61                         try {
62                             writeHeapRecordPrologue();
63                             if (oop instanceof TypeArray) {
64                                 writePrimitiveArray((TypeArray)oop);
65                             } else if (oop instanceof ObjArray) {
66                                 Klass klass = oop.getKlass();
67                                 ObjArrayKlass oak = (ObjArrayKlass) klass;
68                                 Klass bottomType = oak.getBottomKlass();
69                                 if (bottomType instanceof InstanceKlass ||
70                                     bottomType instanceof TypeArrayKlass) {
71                                     writeObjectArray((ObjArray)oop);
72                                 } else {
73                                     writeInternalObject(oop);
74                                 }
75                             } else if (oop instanceof Instance) {
76                                 Instance instance = (Instance) oop;
77                                 Klass klass = instance.getKlass();
78                                 Symbol name = klass.getName();
79                                 if (name.equals(javaLangString)) {
80                                     writeString(instance);
81                                 } else if (name.equals(javaLangClass)) {
82                                     writeClass(instance);
83                                 } else if (name.equals(javaLangThread)) {
84                                     writeThread(instance);
85                                 } else {
86                                     klass = klass.getSuper();
87                                     while (klass != null) {
88                                         name = klass.getName();
89                                         if (name.equals(javaLangThread)) {
90                                             writeThread(instance);
91                                             return false;
92                                         }
93                                         klass = klass.getSuper();
94                                     }
95                                     writeInstance(instance);
96                                 }
97                             } else {
98                                 // not-a-Java-visible oop
99                                 writeInternalObject(oop);
100                             }
101                             writeHeapRecordEpilogue();
102                         } catch (IOException exp) {
103                             throw new RuntimeException(exp);
104                         }
105                         return false;
106                     }
107 
108                     public void epilogue() {
109                         try {
110                             writeHeapFooter();
111                         } catch (IOException exp) {
112                             throw new RuntimeException(exp);
113                         }
114                     }
115                 });
116 
117                 writeHeapRecordPrologue();
118 
119                 // write JavaThreads
120                 writeJavaThreads();
121 
122                 // write JNI global handles
123                 writeGlobalJNIHandles();
124 
125         } catch (RuntimeException re) {
126             handleRuntimeException(re);
127         }
128     }
129 
writeJavaThreads()130     protected void writeJavaThreads() throws IOException {
131         Threads threads = VM.getVM().getThreads();
132         for (int i = 0; i < threads.getNumberOfThreads(); i++) {
133             JavaThread jt = threads.getJavaThreadAt(i);
134             if (jt.getThreadObj() != null) {
135                 // Note that the thread serial number range is 1-to-N
136                 writeJavaThread(jt, i + 1);
137             }
138         }
139     }
140 
writeJavaThread(JavaThread jt, int index)141     protected void writeJavaThread(JavaThread jt, int index)
142                             throws IOException {
143     }
144 
writeGlobalJNIHandles()145     protected void writeGlobalJNIHandles() throws IOException {
146         JNIHandles handles = VM.getVM().getJNIHandles();
147         OopStorage blk = handles.globalHandles();
148         if (blk != null) {
149             try {
150                 blk.oopsDo(new AddressVisitor() {
151                           public void visitAddress(Address handleAddr) {
152                               try {
153                                   if (handleAddr != null) {
154                                       writeGlobalJNIHandle(handleAddr);
155                                   }
156                               } catch (IOException exp) {
157                                   throw new RuntimeException(exp);
158                               }
159                           }
160                               public void visitCompOopAddress(Address handleAddr) {
161                              throw new RuntimeException("Should not reach here. JNIHandles are not compressed");
162                           }
163                        });
164             } catch (RuntimeException re) {
165                 handleRuntimeException(re);
166             }
167         }
168     }
169 
writeGlobalJNIHandle(Address handleAddr)170     protected void writeGlobalJNIHandle(Address handleAddr) throws IOException {
171     }
172 
writeHeapHeader()173     protected void writeHeapHeader() throws IOException {
174     }
175 
176     // write non-Java-visible (hotspot internal) object
writeInternalObject(Oop oop)177     protected void writeInternalObject(Oop oop) throws IOException {
178     }
179 
180     // write Java primitive array
writePrimitiveArray(TypeArray array)181     protected void writePrimitiveArray(TypeArray array) throws IOException {
182         writeObject(array);
183     }
184 
185     // write Java object array
writeObjectArray(ObjArray array)186     protected void writeObjectArray(ObjArray array) throws IOException {
187         writeObject(array);
188     }
189 
writeInstance(Instance instance)190     protected void writeInstance(Instance instance) throws IOException {
191         writeObject(instance);
192     }
193 
writeString(Instance instance)194     protected void writeString(Instance instance) throws IOException {
195         writeInstance(instance);
196     }
197 
writeClass(Instance instance)198     protected void writeClass(Instance instance) throws IOException {
199         writeInstance(instance);
200     }
201 
writeThread(Instance instance)202     protected void writeThread(Instance instance) throws IOException {
203         writeInstance(instance);
204     }
205 
writeObject(Oop oop)206     protected void writeObject(Oop oop) throws IOException {
207         writeObjectHeader(oop);
208         writeObjectFields(oop);
209         writeObjectFooter(oop);
210     }
211 
writeObjectHeader(Oop oop)212     protected void writeObjectHeader(Oop oop) throws IOException {
213     }
214 
215     // write instance fields of given object
writeObjectFields(final Oop oop)216     protected void writeObjectFields(final Oop oop) throws IOException {
217         try {
218             oop.iterate(new DefaultOopVisitor() {
219                     public void doOop(OopField field, boolean isVMField) {
220                         try {
221                                 writeReferenceField(oop, field);
222                         } catch (IOException exp) {
223                             throw new RuntimeException(exp);
224                         }
225                     }
226 
227                     public void doByte(ByteField field, boolean isVMField) {
228                         try {
229                             writeByteField(oop, field);
230                         } catch (IOException exp) {
231                             throw new RuntimeException(exp);
232                         }
233                     }
234 
235                     public void doChar(CharField field, boolean isVMField) {
236                         try {
237                             writeCharField(oop, field);
238                         } catch (IOException exp) {
239                             throw new RuntimeException(exp);
240                         }
241                     }
242 
243                     public void doBoolean(BooleanField field, boolean vField) {
244                         try {
245                             writeBooleanField(oop, field);
246                         } catch (IOException exp) {
247                             throw new RuntimeException(exp);
248                         }
249                     }
250 
251                     public void doShort(ShortField field, boolean isVMField) {
252                         try {
253                             writeShortField(oop, field);
254                         } catch (IOException exp) {
255                             throw new RuntimeException(exp);
256                         }
257                     }
258 
259                     public void doInt(IntField field, boolean isVMField) {
260                         try {
261                             writeIntField(oop, field);
262                         } catch (IOException exp) {
263                             throw new RuntimeException(exp);
264                         }
265                     }
266 
267                     public void doLong(LongField field, boolean isVMField) {
268                         try {
269                             writeLongField(oop, field);
270                         } catch (IOException exp) {
271                             throw new RuntimeException(exp);
272                         }
273                     }
274 
275                     public void doFloat(FloatField field, boolean isVMField) {
276                         try {
277                             writeFloatField(oop, field);
278                         } catch (IOException exp) {
279                             throw new RuntimeException(exp);
280                         }
281                     }
282 
283                     public void doDouble(DoubleField field, boolean vField) {
284                         try {
285                             writeDoubleField(oop, field);
286                         } catch (IOException exp) {
287                             throw new RuntimeException(exp);
288                         }
289                     }
290                 }, false);
291         } catch (RuntimeException re) {
292             handleRuntimeException(re);
293         }
294     }
295 
296     // write instance fields of given object
writeObjectFields(final InstanceKlass oop)297     protected void writeObjectFields(final InstanceKlass oop) throws IOException {
298         try {
299             oop.iterateStaticFields(new DefaultOopVisitor() {
300                     public void doOop(OopField field, boolean isVMField) {
301                         try {
302                             writeReferenceField(null, field);
303                         } catch (IOException exp) {
304                             throw new RuntimeException(exp);
305                         }
306     }
307 
308                     public void doByte(ByteField field, boolean isVMField) {
309                         try {
310                             writeByteField(null, field);
311                         } catch (IOException exp) {
312                             throw new RuntimeException(exp);
313                         }
314                     }
315 
316                     public void doChar(CharField field, boolean isVMField) {
317                         try {
318                             writeCharField(null, field);
319                         } catch (IOException exp) {
320                             throw new RuntimeException(exp);
321                         }
322                     }
323 
324                     public void doBoolean(BooleanField field, boolean vField) {
325                         try {
326                             writeBooleanField(null, field);
327                         } catch (IOException exp) {
328                             throw new RuntimeException(exp);
329                         }
330                     }
331 
332                     public void doShort(ShortField field, boolean isVMField) {
333                         try {
334                             writeShortField(null, field);
335                         } catch (IOException exp) {
336                             throw new RuntimeException(exp);
337                         }
338                     }
339 
340                     public void doInt(IntField field, boolean isVMField) {
341                         try {
342                             writeIntField(null, field);
343                         } catch (IOException exp) {
344                             throw new RuntimeException(exp);
345                         }
346                     }
347 
348                     public void doLong(LongField field, boolean isVMField) {
349                         try {
350                             writeLongField(null, field);
351                         } catch (IOException exp) {
352                             throw new RuntimeException(exp);
353                         }
354                     }
355 
356                     public void doFloat(FloatField field, boolean isVMField) {
357                         try {
358                             writeFloatField(null, field);
359                         } catch (IOException exp) {
360                             throw new RuntimeException(exp);
361                         }
362                     }
363 
364                     public void doDouble(DoubleField field, boolean vField) {
365                         try {
366                             writeDoubleField(null, field);
367                         } catch (IOException exp) {
368                             throw new RuntimeException(exp);
369                         }
370                     }
371                 });
372         } catch (RuntimeException re) {
373             handleRuntimeException(re);
374         }
375     }
376 
377     // object field writers
writeReferenceField(Oop oop, OopField field)378     protected void writeReferenceField(Oop oop, OopField field)
379         throws IOException {
380     }
381 
writeByteField(Oop oop, ByteField field)382     protected void writeByteField(Oop oop, ByteField field)
383         throws IOException {
384     }
385 
writeCharField(Oop oop, CharField field)386     protected void writeCharField(Oop oop, CharField field)
387         throws IOException {
388     }
389 
writeBooleanField(Oop oop, BooleanField field)390     protected void writeBooleanField(Oop oop, BooleanField field)
391         throws IOException {
392     }
393 
writeShortField(Oop oop, ShortField field)394     protected void writeShortField(Oop oop, ShortField field)
395         throws IOException {
396     }
397 
writeIntField(Oop oop, IntField field)398     protected void writeIntField(Oop oop, IntField field)
399         throws IOException {
400     }
401 
writeLongField(Oop oop, LongField field)402     protected void writeLongField(Oop oop, LongField field)
403         throws IOException {
404     }
405 
writeFloatField(Oop oop, FloatField field)406     protected void writeFloatField(Oop oop, FloatField field)
407         throws IOException {
408     }
409 
writeDoubleField(Oop oop, DoubleField field)410     protected void writeDoubleField(Oop oop, DoubleField field)
411         throws IOException {
412     }
413 
writeObjectFooter(Oop oop)414     protected void writeObjectFooter(Oop oop) throws IOException {
415     }
416 
writeHeapFooter()417     protected void writeHeapFooter() throws IOException {
418     }
419 
writeHeapRecordPrologue()420     protected void writeHeapRecordPrologue() throws IOException {
421     }
422 
writeHeapRecordEpilogue()423     protected void writeHeapRecordEpilogue() throws IOException {
424     }
425 
426     // HeapVisitor, OopVisitor methods can't throw any non-runtime
427     // exception. But, derived class write methods (which are called
428     // from visitor callbacks) may throw IOException. Hence, we throw
429     // RuntimeException with origianal IOException as cause from the
430     // visitor methods. This method gets back the original IOException
431     // (if any) and re-throws the same.
handleRuntimeException(RuntimeException re)432     protected void handleRuntimeException(RuntimeException re)
433         throws IOException {
434         Throwable cause = re.getCause();
435         if (cause != null && cause instanceof IOException) {
436             throw (IOException) cause;
437         } else {
438             // some other RuntimeException, just re-throw
439             throw re;
440         }
441     }
442 
443     // whether a given oop is Java visible or hotspot internal?
isJavaVisible(Oop oop)444     protected boolean isJavaVisible(Oop oop) {
445         if (oop instanceof Instance || oop instanceof TypeArray) {
446             return true;
447         } else if (oop instanceof ObjArray) {
448             ObjArrayKlass oak = (ObjArrayKlass) oop.getKlass();
449             Klass bottomKlass = oak.getBottomKlass();
450             return bottomKlass instanceof InstanceKlass ||
451                    bottomKlass instanceof TypeArrayKlass;
452         } else {
453             return false;
454         }
455     }
456 
457     protected String javaLangClass;
458     protected String javaLangString;
459     protected String javaLangThread;
460 }
461