1 /*
   2  * Copyright (c) 2004, 2018, 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
  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 
 130     protected void writeJavaThreads() throws IOException {
 131         Threads threads = VM.getVM().getThreads();
 132         JavaThread jt = threads.first();
 133         int index = 1;
 134         while (jt != null) {
 135             if (jt.getThreadObj() != null) {
 136                 // Note that the thread serial number range is 1-to-N
 137                 writeJavaThread(jt, index);
 138                 index++;
 139             }
 140             jt = jt.next();
 141         }
 142     }
 143 
 144     protected void writeJavaThread(JavaThread jt, int index)
 145                             throws IOException {
 146     }
 147 
 148     protected void writeGlobalJNIHandles() throws IOException {
 149         JNIHandles handles = VM.getVM().getJNIHandles();
 150         OopStorage blk = handles.globalHandles();
 151         if (blk != null) {
 152             try {
 153                 blk.oopsDo(new AddressVisitor() {
 154                           public void visitAddress(Address handleAddr) {
 155                               try {
 156                                   if (handleAddr != null) {
 157                                       writeGlobalJNIHandle(handleAddr);
 158                                   }
 159                               } catch (IOException exp) {
 160                                   throw new RuntimeException(exp);
 161                               }
 162                           }
 163                               public void visitCompOopAddress(Address handleAddr) {
 164                              throw new RuntimeException("Should not reach here. JNIHandles are not compressed");
 165                           }
 166                        });
 167             } catch (RuntimeException re) {
 168                 handleRuntimeException(re);
 169             }
 170         }
 171     }
 172 
 173     protected void writeGlobalJNIHandle(Address handleAddr) throws IOException {
 174     }
 175 
 176     protected void writeHeapHeader() throws IOException {
 177     }
 178 
 179     // write non-Java-visible (hotspot internal) object
 180     protected void writeInternalObject(Oop oop) throws IOException {
 181     }
 182 
 183     // write Java primitive array
 184     protected void writePrimitiveArray(TypeArray array) throws IOException {
 185         writeObject(array);
 186     }
 187 
 188     // write Java object array
 189     protected void writeObjectArray(ObjArray array) throws IOException {
 190         writeObject(array);
 191     }
 192 
 193     protected void writeInstance(Instance instance) throws IOException {
 194         writeObject(instance);
 195     }
 196 
 197     protected void writeString(Instance instance) throws IOException {
 198         writeInstance(instance);
 199     }
 200 
 201     protected void writeClass(Instance instance) throws IOException {
 202         writeInstance(instance);
 203     }
 204 
 205     protected void writeThread(Instance instance) throws IOException {
 206         writeInstance(instance);
 207     }
 208 
 209     protected void writeObject(Oop oop) throws IOException {
 210         writeObjectHeader(oop);
 211         writeObjectFields(oop);
 212         writeObjectFooter(oop);
 213     }
 214 
 215     protected void writeObjectHeader(Oop oop) throws IOException {
 216     }
 217 
 218     // write instance fields of given object
 219     protected void writeObjectFields(final Oop oop) throws IOException {
 220         try {
 221             oop.iterate(new DefaultOopVisitor() {
 222                     public void doOop(OopField field, boolean isVMField) {
 223                         try {
 224                                 writeReferenceField(oop, field);
 225                         } catch (IOException exp) {
 226                             throw new RuntimeException(exp);
 227                         }
 228                     }
 229 
 230                     public void doByte(ByteField field, boolean isVMField) {
 231                         try {
 232                             writeByteField(oop, field);
 233                         } catch (IOException exp) {
 234                             throw new RuntimeException(exp);
 235                         }
 236                     }
 237 
 238                     public void doChar(CharField field, boolean isVMField) {
 239                         try {
 240                             writeCharField(oop, field);
 241                         } catch (IOException exp) {
 242                             throw new RuntimeException(exp);
 243                         }
 244                     }
 245 
 246                     public void doBoolean(BooleanField field, boolean vField) {
 247                         try {
 248                             writeBooleanField(oop, field);
 249                         } catch (IOException exp) {
 250                             throw new RuntimeException(exp);
 251                         }
 252                     }
 253 
 254                     public void doShort(ShortField field, boolean isVMField) {
 255                         try {
 256                             writeShortField(oop, field);
 257                         } catch (IOException exp) {
 258                             throw new RuntimeException(exp);
 259                         }
 260                     }
 261 
 262                     public void doInt(IntField field, boolean isVMField) {
 263                         try {
 264                             writeIntField(oop, field);
 265                         } catch (IOException exp) {
 266                             throw new RuntimeException(exp);
 267                         }
 268                     }
 269 
 270                     public void doLong(LongField field, boolean isVMField) {
 271                         try {
 272                             writeLongField(oop, field);
 273                         } catch (IOException exp) {
 274                             throw new RuntimeException(exp);
 275                         }
 276                     }
 277 
 278                     public void doFloat(FloatField field, boolean isVMField) {
 279                         try {
 280                             writeFloatField(oop, field);
 281                         } catch (IOException exp) {
 282                             throw new RuntimeException(exp);
 283                         }
 284                     }
 285 
 286                     public void doDouble(DoubleField field, boolean vField) {
 287                         try {
 288                             writeDoubleField(oop, field);
 289                         } catch (IOException exp) {
 290                             throw new RuntimeException(exp);
 291                         }
 292                     }
 293                 }, false);
 294         } catch (RuntimeException re) {
 295             handleRuntimeException(re);
 296         }
 297     }
 298 
 299     // write instance fields of given object
 300     protected void writeObjectFields(final InstanceKlass oop) throws IOException {
 301         try {
 302             oop.iterateStaticFields(new DefaultOopVisitor() {
 303                     public void doOop(OopField field, boolean isVMField) {
 304                         try {
 305                             writeReferenceField(null, field);
 306                         } catch (IOException exp) {
 307                             throw new RuntimeException(exp);
 308                         }
 309     }
 310 
 311                     public void doByte(ByteField field, boolean isVMField) {
 312                         try {
 313                             writeByteField(null, field);
 314                         } catch (IOException exp) {
 315                             throw new RuntimeException(exp);
 316                         }
 317                     }
 318 
 319                     public void doChar(CharField field, boolean isVMField) {
 320                         try {
 321                             writeCharField(null, field);
 322                         } catch (IOException exp) {
 323                             throw new RuntimeException(exp);
 324                         }
 325                     }
 326 
 327                     public void doBoolean(BooleanField field, boolean vField) {
 328                         try {
 329                             writeBooleanField(null, field);
 330                         } catch (IOException exp) {
 331                             throw new RuntimeException(exp);
 332                         }
 333                     }
 334 
 335                     public void doShort(ShortField field, boolean isVMField) {
 336                         try {
 337                             writeShortField(null, field);
 338                         } catch (IOException exp) {
 339                             throw new RuntimeException(exp);
 340                         }
 341                     }
 342 
 343                     public void doInt(IntField field, boolean isVMField) {
 344                         try {
 345                             writeIntField(null, field);
 346                         } catch (IOException exp) {
 347                             throw new RuntimeException(exp);
 348                         }
 349                     }
 350 
 351                     public void doLong(LongField field, boolean isVMField) {
 352                         try {
 353                             writeLongField(null, field);
 354                         } catch (IOException exp) {
 355                             throw new RuntimeException(exp);
 356                         }
 357                     }
 358 
 359                     public void doFloat(FloatField field, boolean isVMField) {
 360                         try {
 361                             writeFloatField(null, field);
 362                         } catch (IOException exp) {
 363                             throw new RuntimeException(exp);
 364                         }
 365                     }
 366 
 367                     public void doDouble(DoubleField field, boolean vField) {
 368                         try {
 369                             writeDoubleField(null, field);
 370                         } catch (IOException exp) {
 371                             throw new RuntimeException(exp);
 372                         }
 373                     }
 374                 });
 375         } catch (RuntimeException re) {
 376             handleRuntimeException(re);
 377         }
 378     }
 379 
 380     // object field writers
 381     protected void writeReferenceField(Oop oop, OopField field)
 382         throws IOException {
 383     }
 384 
 385     protected void writeByteField(Oop oop, ByteField field)
 386         throws IOException {
 387     }
 388 
 389     protected void writeCharField(Oop oop, CharField field)
 390         throws IOException {
 391     }
 392 
 393     protected void writeBooleanField(Oop oop, BooleanField field)
 394         throws IOException {
 395     }
 396 
 397     protected void writeShortField(Oop oop, ShortField field)
 398         throws IOException {
 399     }
 400 
 401     protected void writeIntField(Oop oop, IntField field)
 402         throws IOException {
 403     }
 404 
 405     protected void writeLongField(Oop oop, LongField field)
 406         throws IOException {
 407     }
 408 
 409     protected void writeFloatField(Oop oop, FloatField field)
 410         throws IOException {
 411     }
 412 
 413     protected void writeDoubleField(Oop oop, DoubleField field)
 414         throws IOException {
 415     }
 416 
 417     protected void writeObjectFooter(Oop oop) throws IOException {
 418     }
 419 
 420     protected void writeHeapFooter() throws IOException {
 421     }
 422 
 423     protected void writeHeapRecordPrologue() throws IOException {
 424     }
 425 
 426     protected void writeHeapRecordEpilogue() throws IOException {
 427     }
 428 
 429     // HeapVisitor, OopVisitor methods can't throw any non-runtime
 430     // exception. But, derived class write methods (which are called
 431     // from visitor callbacks) may throw IOException. Hence, we throw
 432     // RuntimeException with origianal IOException as cause from the
 433     // visitor methods. This method gets back the original IOException
 434     // (if any) and re-throws the same.
 435     protected void handleRuntimeException(RuntimeException re)
 436         throws IOException {
 437         Throwable cause = re.getCause();
 438         if (cause != null && cause instanceof IOException) {
 439             throw (IOException) cause;
 440         } else {
 441             // some other RuntimeException, just re-throw
 442             throw re;
 443         }
 444     }
 445 
 446     // whether a given oop is Java visible or hotspot internal?
 447     protected boolean isJavaVisible(Oop oop) {
 448         if (oop instanceof Instance || oop instanceof TypeArray) {
 449             return true;
 450         } else if (oop instanceof ObjArray) {
 451             ObjArrayKlass oak = (ObjArrayKlass) oop.getKlass();
 452             Klass bottomKlass = oak.getBottomKlass();
 453             return bottomKlass instanceof InstanceKlass ||
 454                    bottomKlass instanceof TypeArrayKlass;
 455         } else {
 456             return false;
 457         }
 458     }
 459 
 460     protected String javaLangClass;
 461     protected String javaLangString;
 462     protected String javaLangThread;
 463 }