1 /*
  2  * Copyright (c) 1997, 2023, 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 /*
 26  * The Original Code is HAT. The Initial Developer of the
 27  * Original Code is Bill Foote, with contributions from others
 28  * at JavaSoft/Sun.
 29  */
 30 
 31 package jdk.test.lib.hprof.parser;
 32 
 33 import java.io.*;
 34 import java.util.Date;
 35 import java.util.Hashtable;
 36 import java.util.Map;
 37 import jdk.test.lib.hprof.model.ArrayTypeCodes;
 38 import jdk.test.lib.hprof.model.*;
 39 
 40 /**
 41  * Object that's used to read a hprof file.
 42  *
 43  * @author      Bill Foote
 44  */
 45 
 46 public class HprofReader extends Reader /* imports */ implements ArrayTypeCodes {
 47 
 48     final static int MAGIC_NUMBER = 0x4a415641;
 49     // That's "JAVA", the first part of "JAVA PROFILE ..."
 50     private final static String[] VERSIONS = {
 51             " PROFILE 1.0\0",
 52             " PROFILE 1.0.1\0",
 53             " PROFILE 1.0.2\0",
 54     };
 55 
 56     private final static int VERSION_JDK12BETA3 = 0;
 57     private final static int VERSION_JDK12BETA4 = 1;
 58     private final static int VERSION_JDK6       = 2;
 59     // These version numbers are indices into VERSIONS.  The instance data
 60     // member version is set to one of these, and it drives decisions when
 61     // reading the file.
 62     //
 63     // Version 1.0.1 added HPROF_GC_PRIM_ARRAY_DUMP, which requires no
 64     // version-sensitive parsing.
 65     //
 66     // Version 1.0.1 changed the type of a constant pool entry from a signature
 67     // to a typecode.
 68     //
 69     // Version 1.0.2 added HPROF_HEAP_DUMP_SEGMENT and HPROF_HEAP_DUMP_END
 70     // to allow a large heap to be dumped as a sequence of heap dump segments.
 71     //
 72     // The HPROF agent in J2SE 1.2 through to 5.0 generate a version 1.0.1
 73     // file. In Java SE 6.0 the version is either 1.0.1 or 1.0.2 depending on
 74     // the size of the heap (normally it will be 1.0.1 but for multi-GB
 75     // heaps the heap dump will not fit in a HPROF_HEAP_DUMP record so the
 76     // dump is generated as version 1.0.2).
 77 
 78     //
 79     // Record types:
 80     //
 81     static final int HPROF_UTF8          = 0x01;
 82     static final int HPROF_LOAD_CLASS    = 0x02;
 83     static final int HPROF_UNLOAD_CLASS  = 0x03;
 84     static final int HPROF_FRAME         = 0x04;
 85     static final int HPROF_TRACE         = 0x05;
 86     static final int HPROF_ALLOC_SITES   = 0x06;
 87     static final int HPROF_HEAP_SUMMARY  = 0x07;
 88 
 89     static final int HPROF_START_THREAD  = 0x0a;
 90     static final int HPROF_END_THREAD    = 0x0b;
 91 
 92     static final int HPROF_HEAP_DUMP     = 0x0c;
 93 
 94     static final int HPROF_CPU_SAMPLES   = 0x0d;
 95     static final int HPROF_CONTROL_SETTINGS = 0x0e;
 96     static final int HPROF_LOCKSTATS_WAIT_TIME = 0x10;
 97     static final int HPROF_LOCKSTATS_HOLD_TIME = 0x11;
 98 
 99     static final int HPROF_GC_ROOT_UNKNOWN       = 0xff;
100     static final int HPROF_GC_ROOT_JNI_GLOBAL    = 0x01;
101     static final int HPROF_GC_ROOT_JNI_LOCAL     = 0x02;
102     static final int HPROF_GC_ROOT_JAVA_FRAME    = 0x03;
103     static final int HPROF_GC_ROOT_NATIVE_STACK  = 0x04;
104     static final int HPROF_GC_ROOT_STICKY_CLASS  = 0x05;
105     static final int HPROF_GC_ROOT_THREAD_BLOCK  = 0x06;
106     static final int HPROF_GC_ROOT_MONITOR_USED  = 0x07;
107     static final int HPROF_GC_ROOT_THREAD_OBJ    = 0x08;
108 
109     static final int HPROF_GC_CLASS_DUMP         = 0x20;
110     static final int HPROF_GC_INSTANCE_DUMP      = 0x21;
111     static final int HPROF_GC_OBJ_ARRAY_DUMP         = 0x22;
112     static final int HPROF_GC_PRIM_ARRAY_DUMP         = 0x23;
113 
114     static final int HPROF_HEAP_DUMP_SEGMENT     = 0x1c;
115     static final int HPROF_HEAP_DUMP_END         = 0x2c;
116 
117     private final static int T_CLASS = 2;
118 
119     private int version;        // The version of .hprof being read
120 
121     private int debugLevel;
122     private long currPos;        // Current position in the file
123 
124     private int dumpsToSkip;
125     private boolean callStack;  // If true, read the call stack of objects
126 
127     private int identifierSize;         // Size, in bytes, of identifiers.
128     private Hashtable<Long, String> names;
129 
130     // Hashtable<Integer, ThreadObject>, used to map the thread sequence number
131     // (aka "serial number") to the thread object ID for
132     // HPROF_GC_ROOT_THREAD_OBJ.  ThreadObject is a trivial inner class,
133     // at the end of this file.
134     private Hashtable<Integer, ThreadObject> threadObjects;
135 
136     // Hashtable<Long, String>, maps class object ID to class name
137     // (with / converted to .)
138     private Hashtable<Long, String> classNameFromObjectID;
139 
140     // Hashtable<Integer, Integer>, maps class serial # to class object ID
141     private Hashtable<Integer, String> classNameFromSerialNo;
142 
143     // Hashtable<Long, StackFrame> maps stack frame ID to StackFrame.
144     // Null if we're not tracking them.
145     private Hashtable<Long, StackFrame> stackFrames;
146 
147     // Hashtable<Integer, StackTrace> maps stack frame ID to StackTrace
148     // Null if we're not tracking them.
149     private Hashtable<Integer, StackTrace> stackTraces;
150 
151     private Snapshot snapshot;
152 
153     public static boolean verifyMagicNumber(int numberRead) {
154         return (numberRead == MAGIC_NUMBER);
155     }
156 
157     public HprofReader(ReadBuffer readBuffer, PositionDataInputStream in,
158                        int dumpNumber, boolean callStack, int debugLevel)
159                        throws IOException {
160         super(in);
161         this.snapshot = new Snapshot(readBuffer);
162         this.dumpsToSkip = dumpNumber - 1;
163         this.callStack = callStack;
164         this.debugLevel = debugLevel;
165         names = new Hashtable<Long, String>();
166         threadObjects = new Hashtable<Integer, ThreadObject>(43);
167         classNameFromObjectID = new Hashtable<Long, String>();
168         if (callStack) {
169             stackFrames = new Hashtable<Long, StackFrame>(43);
170             stackTraces = new Hashtable<Integer, StackTrace>(43);
171             classNameFromSerialNo = new Hashtable<Integer, String>();
172         }
173     }
174 
175     public HprofReader(String fileName, PositionDataInputStream in,
176                        int dumpNumber, boolean callStack, int debugLevel)
177                        throws IOException {
178         this(MappedReadBuffer.create(new RandomAccessFile(fileName, "r")),
179             in, dumpNumber, callStack, debugLevel);
180     }
181 
182     public Snapshot read() throws IOException {
183         currPos = 4;    // 4 because of the magic number
184         version = readVersionHeader();
185         identifierSize = in.readInt();
186         snapshot.setIdentifierSize(identifierSize);
187         if (version >= VERSION_JDK12BETA4) {
188             snapshot.setNewStyleArrayClass(true);
189         } else {
190             snapshot.setNewStyleArrayClass(false);
191         }
192 
193         currPos += 4;
194         if (identifierSize != 4 && identifierSize != 8) {
195             throw new IOException("I'm sorry, but I can't deal with an identifier size of " + identifierSize + ".  I can only deal with 4 or 8.");
196         }
197         System.out.println("Dump file created " + (new Date(in.readLong())));
198         currPos += 8;
199 
200         for (;;) {
201             int type;
202             try {
203                 type = in.readUnsignedByte();
204             } catch (EOFException ignored) {
205                 break;
206             }
207             in.readInt();       // Timestamp of this record
208             // Length of record: readInt() will return negative value for record
209             // length >2GB.  so store 32bit value in long to keep it unsigned.
210             long length = in.readInt() & 0xffffffffL;
211             if (debugLevel > 0) {
212                 System.out.println("Read record type " + type
213                                    + ", length " + length
214                                    + " at position " + toHex(currPos));
215             }
216             if (length < 0) {
217                 throw new IOException("Bad record length of " + length
218                                       + " at byte " + toHex(currPos+5)
219                                       + " of file.");
220             }
221             currPos += 9 + length;
222             switch (type) {
223                 case HPROF_UTF8: {
224                     long id = readID();
225                     byte[] chars = new byte[(int)length - identifierSize];
226                     in.readFully(chars);
227                     names.put(id, new String(chars));
228                     break;
229                 }
230                 case HPROF_LOAD_CLASS: {
231                     int serialNo = in.readInt();        // Not used
232                     long classID = readID();
233                     int stackTraceSerialNo = in.readInt();
234                     long classNameID = readID();
235                     Long classIdI = classID;
236                     String nm = getNameFromID(classNameID).replace('/', '.');
237                     classNameFromObjectID.put(classIdI, nm);
238                     if (classNameFromSerialNo != null) {
239                         classNameFromSerialNo.put(serialNo, nm);
240                     }
241                     break;
242                 }
243 
244                 case HPROF_HEAP_DUMP: {
245                     if (dumpsToSkip <= 0) {
246                         try {
247                             readHeapDump(length, currPos);
248                         } catch (EOFException exp) {
249                             handleEOF(exp, snapshot);
250                         }
251                         if (debugLevel > 0) {
252                             System.out.println("    Finished processing instances in heap dump.");
253                         }
254                         return snapshot;
255                     } else {
256                         dumpsToSkip--;
257                         skipBytes(length);
258                     }
259                     break;
260                 }
261 
262                 case HPROF_HEAP_DUMP_END: {
263                     if (version >= VERSION_JDK6) {
264                         if (dumpsToSkip <= 0) {
265                             skipBytes(length);  // should be no-op
266                             return snapshot;
267                         } else {
268                             // skip this dump (of the end record for a sequence of dump segments)
269                             dumpsToSkip--;
270                         }
271                     } else {
272                         // HPROF_HEAP_DUMP_END only recognized in >= 1.0.2
273                         warn("Ignoring unrecognized record type " + type);
274                     }
275                     skipBytes(length);  // should be no-op
276                     break;
277                 }
278 
279                 case HPROF_HEAP_DUMP_SEGMENT: {
280                     if (version >= VERSION_JDK6) {
281                         if (dumpsToSkip <= 0) {
282                             try {
283                                 // read the dump segment
284                                 readHeapDump(length, currPos);
285                             } catch (EOFException exp) {
286                                 handleEOF(exp, snapshot);
287                             }
288                         } else {
289                             // all segments comprising the heap dump will be skipped
290                             skipBytes(length);
291                         }
292                     } else {
293                         // HPROF_HEAP_DUMP_SEGMENT only recognized in >= 1.0.2
294                         warn("Ignoring unrecognized record type " + type);
295                         skipBytes(length);
296                     }
297                     break;
298                 }
299 
300                 case HPROF_FRAME: {
301                     if (stackFrames == null) {
302                         skipBytes(length);
303                     } else {
304                         long id = readID();
305                         String methodName = getNameFromID(readID());
306                         String methodSig = getNameFromID(readID());
307                         String sourceFile = getNameFromID(readID());
308                         int classSer = in.readInt();
309                         String className = classNameFromSerialNo.get(classSer);
310                         int lineNumber = in.readInt();
311                         if (lineNumber < StackFrame.LINE_NUMBER_NATIVE) {
312                             warn("Weird stack frame line number:  " + lineNumber);
313                             lineNumber = StackFrame.LINE_NUMBER_UNKNOWN;
314                         }
315                         stackFrames.put(id,
316                                         new StackFrame(methodName, methodSig,
317                                                        className, sourceFile,
318                                                        lineNumber));
319                     }
320                     break;
321                 }
322                 case HPROF_TRACE: {
323                     if (stackTraces == null) {
324                         skipBytes(length);
325                     } else {
326                         int serialNo = in.readInt();
327                         int threadSeq = in.readInt();   // Not used
328                         StackFrame[] frames = new StackFrame[in.readInt()];
329                         for (int i = 0; i < frames.length; i++) {
330                             long fid = readID();
331                             frames[i] = stackFrames.get(fid);
332                             if (frames[i] == null) {
333                                 throw new IOException("Stack frame " + toHex(fid) + " not found");
334                             }
335                         }
336                         stackTraces.put(serialNo,
337                                         new StackTrace(frames));
338                     }
339                     break;
340                 }
341                 case HPROF_UNLOAD_CLASS:
342                 case HPROF_ALLOC_SITES:
343                 case HPROF_START_THREAD:
344                 case HPROF_END_THREAD:
345                 case HPROF_HEAP_SUMMARY:
346                 case HPROF_CPU_SAMPLES:
347                 case HPROF_CONTROL_SETTINGS:
348                 case HPROF_LOCKSTATS_WAIT_TIME:
349                 case HPROF_LOCKSTATS_HOLD_TIME:
350                 {
351                     // Ignore these record types
352                     skipBytes(length);
353                     break;
354                 }
355                 default: {
356                     skipBytes(length);
357                     warn("Ignoring unrecognized record type " + type);
358                 }
359             }
360         }
361 
362         return snapshot;
363     }
364 
365     public String printStackTraces() {
366         StringBuffer output = new StringBuffer();
367         for (Map.Entry<Integer, StackTrace> entry : stackTraces.entrySet()) {
368             StackFrame[] frames = entry.getValue().getFrames();
369             output.append("SerialNo " + entry.getKey() + "\n");
370             for (int i = 0; i < frames.length; i++) {
371                 output.append("  " + frames[i].getClassName() + "." + frames[i].getMethodName()
372                         + frames[i].getMethodSignature() + " (" + frames[i].getSourceFileName()
373                         + ":" + frames[i].getLineNumber() + ")" + "\n");
374             }
375         }
376 
377         System.out.println(output);
378         return output.toString();
379     }
380 
381     private void skipBytes(long length) throws IOException {
382         while (length > 0) {
383             long skipped = in.skip(length);
384             if (skipped == 0) {
385                 // EOF or other problem, throw exception
386                 throw new EOFException("Couldn't skip enough bytes");
387             }
388             length -= skipped;
389         }
390     }
391 
392     private int readVersionHeader() throws IOException {
393         int candidatesLeft = VERSIONS.length;
394         boolean[] matched = new boolean[VERSIONS.length];
395         for (int i = 0; i < candidatesLeft; i++) {
396             matched[i] = true;
397         }
398 
399         int pos = 0;
400         while (candidatesLeft > 0) {
401             char c = (char) in.readByte();
402             currPos++;
403             for (int i = 0; i < VERSIONS.length; i++) {
404                 if (matched[i]) {
405                     if (c != VERSIONS[i].charAt(pos)) {   // Not matched
406                         matched[i] = false;
407                         --candidatesLeft;
408                     } else if (pos == VERSIONS[i].length() - 1) {  // Full match
409                         return i;
410                     }
411                 }
412             }
413             ++pos;
414         }
415         throw new IOException("Version string not recognized at byte " + (pos+3));
416     }
417 
418     private void readHeapDump(long bytesLeft, long posAtEnd) throws IOException {
419         while (bytesLeft > 0) {
420             int type = in.readUnsignedByte();
421             if (debugLevel > 0) {
422                 System.out.println("    Read heap sub-record type " + type
423                                    + " at position "
424                                    + toHex(posAtEnd - bytesLeft));
425             }
426             bytesLeft--;
427             switch(type) {
428                 case HPROF_GC_ROOT_UNKNOWN: {
429                     long id = readID();
430                     bytesLeft -= identifierSize;
431                     snapshot.addRoot(new Root(id, 0, Root.UNKNOWN, ""));
432                     break;
433                 }
434                 case HPROF_GC_ROOT_THREAD_OBJ: {
435                     long id = readID();
436                     int threadSeq = in.readInt();
437                     int stackSeq = in.readInt();
438                     bytesLeft -= identifierSize + 8;
439                     StackTrace st = getStackTraceFromSerial(stackSeq);
440                     ThreadObject threadObj = new ThreadObject(id, st);
441                     threadObjects.put(threadSeq, threadObj);
442                     snapshot.addThreadObject(threadObj);
443                     break;
444                 }
445                 case HPROF_GC_ROOT_JNI_GLOBAL: {
446                     long id = readID();
447                     long globalRefId = readID();        // Ignored, for now
448                     bytesLeft -= 2*identifierSize;
449                     snapshot.addRoot(new Root(id, 0, Root.NATIVE_STATIC, ""));
450                     break;
451                 }
452                 case HPROF_GC_ROOT_JNI_LOCAL: {
453                     long id = readID();
454                     int threadSeq = in.readInt();
455                     int depth = in.readInt();
456                     bytesLeft -= identifierSize + 8;
457                     ThreadObject to = getThreadObjectFromSequence(threadSeq);
458                     StackTrace st = to.getStackTrace();
459                     if (st != null) {
460                         st = st.traceForDepth(depth+1);
461                     }
462                     snapshot.addRoot(new Root(id, to.getId(),
463                                               Root.NATIVE_LOCAL, "", st));
464                     break;
465                 }
466                 case HPROF_GC_ROOT_JAVA_FRAME: {
467                     long id = readID();
468                     int threadSeq = in.readInt();
469                     int depth = in.readInt();
470                     bytesLeft -= identifierSize + 8;
471                     ThreadObject to = getThreadObjectFromSequence(threadSeq);
472                     StackTrace st = to.getStackTrace();;
473                     if (st != null) {
474                         st = st.traceForDepth(depth+1);
475                     }
476                     snapshot.addRoot(new Root(id, to.getId(),
477                                               Root.JAVA_LOCAL, "", st));
478                     break;
479                 }
480                 case HPROF_GC_ROOT_NATIVE_STACK: {
481                     long id = readID();
482                     int threadSeq = in.readInt();
483                     bytesLeft -= identifierSize + 4;
484                     ThreadObject to = getThreadObjectFromSequence(threadSeq);
485                     StackTrace st = to.getStackTrace();;
486                     snapshot.addRoot(new Root(id, to.getId(),
487                                               Root.NATIVE_STACK, "", st));
488                     break;
489                 }
490                 case HPROF_GC_ROOT_STICKY_CLASS: {
491                     long id = readID();
492                     bytesLeft -= identifierSize;
493                     snapshot.addRoot(new Root(id, 0, Root.SYSTEM_CLASS, ""));
494                     break;
495                 }
496                 case HPROF_GC_ROOT_THREAD_BLOCK: {
497                     long id = readID();
498                     int threadSeq = in.readInt();
499                     bytesLeft -= identifierSize + 4;
500                     ThreadObject to = getThreadObjectFromSequence(threadSeq);
501                     StackTrace st = to.getStackTrace();
502                     snapshot.addRoot(new Root(id, to.getId(),
503                                      Root.THREAD_BLOCK, "", st));
504                     break;
505                 }
506                 case HPROF_GC_ROOT_MONITOR_USED: {
507                     long id = readID();
508                     bytesLeft -= identifierSize;
509                     snapshot.addRoot(new Root(id, 0, Root.BUSY_MONITOR, ""));
510                     break;
511                 }
512                 case HPROF_GC_CLASS_DUMP: {
513                     int bytesRead = readClass();
514                     bytesLeft -= bytesRead;
515                     break;
516                 }
517                 case HPROF_GC_INSTANCE_DUMP: {
518                     int bytesRead = readInstance();
519                     bytesLeft -= bytesRead;
520                     break;
521                 }
522                 case HPROF_GC_OBJ_ARRAY_DUMP: {
523                     long bytesRead = readArray(false);
524                     bytesLeft -= bytesRead;
525                     break;
526                 }
527                 case HPROF_GC_PRIM_ARRAY_DUMP: {
528                     long bytesRead = readArray(true);
529                     bytesLeft -= bytesRead;
530                     break;
531                 }
532                 default: {
533                     throw new IOException("Unrecognized heap dump sub-record type:  " + type);
534                 }
535             }
536         }
537         if (bytesLeft != 0) {
538             warn("Error reading heap dump or heap dump segment:  Byte count is " + bytesLeft + " instead of 0");
539             skipBytes(bytesLeft);
540         }
541         if (debugLevel > 0) {
542             System.out.println("    Finished heap sub-records.");
543         }
544     }
545 
546     private long readID() throws IOException {
547         return (identifierSize == 4)?
548             (Snapshot.SMALL_ID_MASK & (long)in.readInt()) : in.readLong();
549     }
550 
551     //
552     // Read a java value.  If result is non-null, it's expected to be an
553     // array of one element.  We use it to fake multiple return values.
554     // @returns the number of bytes read
555     //
556     private int readValue(JavaThing[] resultArr) throws IOException {
557         byte type = in.readByte();
558         return 1 + readValueForType(type, resultArr);
559     }
560 
561     private int readValueForType(byte type, JavaThing[] resultArr)
562             throws IOException {
563         if (version >= VERSION_JDK12BETA4) {
564             type = signatureFromTypeId(type);
565         }
566         return readValueForTypeSignature(type, resultArr);
567     }
568 
569     private int readValueForTypeSignature(byte type, JavaThing[] resultArr)
570             throws IOException {
571         switch (type) {
572             case '[':
573             case 'L': {
574                 long id = readID();
575                 if (resultArr != null) {
576                     resultArr[0] = new JavaObjectRef(id);
577                 }
578                 return identifierSize;
579             }
580             case 'Z': {
581                 int b = in.readByte();
582                 if (b != 0 && b != 1) {
583                     warn("Illegal boolean value read");
584                 }
585                 if (resultArr != null) {
586                     resultArr[0] = new JavaBoolean(b != 0);
587                 }
588                 return 1;
589             }
590             case 'B': {
591                 byte b = in.readByte();
592                 if (resultArr != null) {
593                     resultArr[0] = new JavaByte(b);
594                 }
595                 return 1;
596             }
597             case 'S': {
598                 short s = in.readShort();
599                 if (resultArr != null) {
600                     resultArr[0] = new JavaShort(s);
601                 }
602                 return 2;
603             }
604             case 'C': {
605                 char ch = in.readChar();
606                 if (resultArr != null) {
607                     resultArr[0] = new JavaChar(ch);
608                 }
609                 return 2;
610             }
611             case 'I': {
612                 int val = in.readInt();
613                 if (resultArr != null) {
614                     resultArr[0] = new JavaInt(val);
615                 }
616                 return 4;
617             }
618             case 'J': {
619                 long val = in.readLong();
620                 if (resultArr != null) {
621                     resultArr[0] = new JavaLong(val);
622                 }
623                 return 8;
624             }
625             case 'F': {
626                 float val = in.readFloat();
627                 if (resultArr != null) {
628                     resultArr[0] = new JavaFloat(val);
629                 }
630                 return 4;
631             }
632             case 'D': {
633                 double val = in.readDouble();
634                 if (resultArr != null) {
635                     resultArr[0] = new JavaDouble(val);
636                 }
637                 return 8;
638             }
639             default: {
640                 throw new IOException("Bad value signature:  " + type);
641             }
642         }
643     }
644 
645     private ThreadObject getThreadObjectFromSequence(int threadSeq)
646             throws IOException {
647         ThreadObject to = threadObjects.get(threadSeq);
648         if (to == null) {
649             throw new IOException("Thread " + threadSeq +
650                                   " not found for JNI local ref");
651         }
652         return to;
653     }
654 
655     private String getNameFromID(long id) throws IOException {
656         return getNameFromID(Long.valueOf(id));
657     }
658 
659     private String getNameFromID(Long id) throws IOException {
660         if (id.longValue() == 0L) {
661             return "";
662         }
663         String result = names.get(id);
664         if (result == null) {
665             warn("Name not found at " + toHex(id.longValue()));
666             return "unresolved name " + toHex(id.longValue());
667         }
668         return result;
669     }
670 
671     private StackTrace getStackTraceFromSerial(int ser) throws IOException {
672         if (stackTraces == null) {
673             return null;
674         }
675         StackTrace result = stackTraces.get(ser);
676         if (result == null) {
677             warn("Stack trace not found for serial # " + ser);
678         }
679         return result;
680     }
681 
682     //
683     // Handle a HPROF_GC_CLASS_DUMP
684     // Return number of bytes read
685     //
686     private int readClass() throws IOException {
687         long id = readID();
688         StackTrace stackTrace = getStackTraceFromSerial(in.readInt());
689         long superId = readID();
690         long classLoaderId = readID();
691         long signersId = readID();
692         long protDomainId = readID();
693         long reserved1 = readID();
694         long reserved2 = readID();
695         int instanceSize = in.readInt();
696         int bytesRead = 7 * identifierSize + 8;
697 
698         int numConstPoolEntries = in.readUnsignedShort();
699         bytesRead += 2;
700         for (int i = 0; i < numConstPoolEntries; i++) {
701             int index = in.readUnsignedShort(); // unused
702             bytesRead += 2;
703             bytesRead += readValue(null);       // We ignore the values
704         }
705 
706         int numStatics = in.readUnsignedShort();
707         bytesRead += 2;
708         JavaThing[] valueBin = new JavaThing[1];
709         JavaStatic[] statics = new JavaStatic[numStatics];
710         for (int i = 0; i < numStatics; i++) {
711             long nameId = readID();
712             bytesRead += identifierSize;
713             byte type = in.readByte();
714             bytesRead++;
715             bytesRead += readValueForType(type, valueBin);
716             String fieldName = getNameFromID(nameId);
717             if (version >= VERSION_JDK12BETA4) {
718                 type = signatureFromTypeId(type);
719             }
720             String signature = "" + ((char) type);
721             JavaField f = new JavaField(fieldName, signature);
722             statics[i] = new JavaStatic(f, valueBin[0]);
723         }
724 
725         int numFields = in.readUnsignedShort();
726         bytesRead += 2;
727         JavaField[] fields = new JavaField[numFields];
728         for (int i = 0; i < numFields; i++) {
729             long nameId = readID();
730             bytesRead += identifierSize;
731             byte type = in.readByte();
732             bytesRead++;
733             String fieldName = getNameFromID(nameId);
734             if (version >= VERSION_JDK12BETA4) {
735                 type = signatureFromTypeId(type);
736             }
737             String signature = "" + ((char) type);
738             fields[i] = new JavaField(fieldName, signature);
739         }
740         String name = classNameFromObjectID.get(id);
741         if (name == null) {
742             warn("Class name not found for " + toHex(id));
743             name = "unknown-name@" + toHex(id);
744         }
745         JavaClass c = new JavaClass(id, name, superId, classLoaderId, signersId,
746                                     protDomainId, fields, statics,
747                                     instanceSize);
748         snapshot.addClass(id, c);
749         snapshot.setSiteTrace(c, stackTrace);
750 
751         return bytesRead;
752     }
753 
754     private String toHex(long addr) {
755         return jdk.test.lib.hprof.util.Misc.toHex(addr);
756     }
757 
758     //
759     // Handle a HPROF_GC_INSTANCE_DUMP
760     // Return number of bytes read
761     //
762     private int readInstance() throws IOException {
763         long start = in.position();
764         long id = readID();
765         StackTrace stackTrace = getStackTraceFromSerial(in.readInt());
766         long classID = readID();
767         JavaClass searchedClass = snapshot.findClass(
768                                   "0x" + Long.toHexString(classID));
769         if (searchedClass == null) {
770             throw new IOException(
771                 "Class Record for 0x" + Long.toHexString(classID) + " not found");
772         }
773         int bytesFollowing = in.readInt();
774         int bytesRead = (2 * identifierSize) + 8 + bytesFollowing;
775         JavaObject jobj = new JavaObject(classID, start);
776         skipBytes(bytesFollowing);
777         snapshot.addHeapObject(id, jobj);
778         snapshot.setSiteTrace(jobj, stackTrace);
779         return bytesRead;
780     }
781 
782     //
783     // Handle a HPROF_GC_OBJ_ARRAY_DUMP or HPROF_GC_PRIM_ARRAY_DUMP
784     // Return number of bytes read
785     //
786     private long readArray(boolean isPrimitive) throws IOException {
787         long start = in.position();
788         long id = readID();
789         StackTrace stackTrace = getStackTraceFromSerial(in.readInt());
790         int num = in.readInt();
791         long bytesRead = identifierSize + 8;
792         long elementClassID;
793         if (isPrimitive) {
794             elementClassID = in.readByte();
795             bytesRead++;
796         } else {
797             elementClassID = readID();
798             bytesRead += identifierSize;
799         }
800 
801         // Check for primitive arrays:
802         byte primitiveSignature = 0x00;
803         int elSize = 0;
804         if (isPrimitive || version < VERSION_JDK12BETA4) {
805             switch ((int)elementClassID) {
806                 case T_BOOLEAN: {
807                     primitiveSignature = (byte) 'Z';
808                     elSize = 1;
809                     break;
810                 }
811                 case T_CHAR: {
812                     primitiveSignature = (byte) 'C';
813                     elSize = 2;
814                     break;
815                 }
816                 case T_FLOAT: {
817                     primitiveSignature = (byte) 'F';
818                     elSize = 4;
819                     break;
820                 }
821                 case T_DOUBLE: {
822                     primitiveSignature = (byte) 'D';
823                     elSize = 8;
824                     break;
825                 }
826                 case T_BYTE: {
827                     primitiveSignature = (byte) 'B';
828                     elSize = 1;
829                     break;
830                 }
831                 case T_SHORT: {
832                     primitiveSignature = (byte) 'S';
833                     elSize = 2;
834                     break;
835                 }
836                 case T_INT: {
837                     primitiveSignature = (byte) 'I';
838                     elSize = 4;
839                     break;
840                 }
841                 case T_LONG: {
842                     primitiveSignature = (byte) 'J';
843                     elSize = 8;
844                     break;
845                 }
846             }
847             if (version >= VERSION_JDK12BETA4 && primitiveSignature == 0x00) {
848                 throw new IOException("Unrecognized typecode:  "
849                                         + elementClassID);
850             }
851         }
852         if (primitiveSignature != 0x00) {
853             long size = elSize * (long)num;
854             bytesRead += size;
855             JavaValueArray va = new JavaValueArray(primitiveSignature, start);
856             skipBytes(size);
857             snapshot.addHeapObject(id, va);
858             snapshot.setSiteTrace(va, stackTrace);
859         } else {
860             long sz = (long)num * identifierSize;
861             bytesRead += sz;
862             JavaObjectArray arr = new JavaObjectArray(elementClassID, start);
863             skipBytes(sz);
864             snapshot.addHeapObject(id, arr);
865             snapshot.setSiteTrace(arr, stackTrace);
866         }
867         return bytesRead;
868     }
869 
870     private byte signatureFromTypeId(byte typeId) throws IOException {
871         switch (typeId) {
872             case T_CLASS: {
873                 return (byte) 'L';
874             }
875             case T_BOOLEAN: {
876                 return (byte) 'Z';
877             }
878             case T_CHAR: {
879                 return (byte) 'C';
880             }
881             case T_FLOAT: {
882                 return (byte) 'F';
883             }
884             case T_DOUBLE: {
885                 return (byte) 'D';
886             }
887             case T_BYTE: {
888                 return (byte) 'B';
889             }
890             case T_SHORT: {
891                 return (byte) 'S';
892             }
893             case T_INT: {
894                 return (byte) 'I';
895             }
896             case T_LONG: {
897                 return (byte) 'J';
898             }
899             default: {
900                 throw new IOException("Invalid type id of " + typeId);
901             }
902         }
903     }
904 
905     private void handleEOF(EOFException exp, Snapshot snapshot) {
906         if (debugLevel > 0) {
907             exp.printStackTrace();
908         }
909         warn("Unexpected EOF. Will miss information...");
910         // we have EOF, we have to tolerate missing references
911         snapshot.setUnresolvedObjectsOK(true);
912     }
913 
914     private void warn(String msg) {
915         System.out.println("WARNING: " + msg);
916     }
917 
918 }