• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.hit;
18 
19 import java.io.ByteArrayOutputStream;
20 import java.io.DataInputStream;
21 import java.io.InputStream;
22 import java.io.EOFException;
23 import java.io.IOException;
24 import java.util.HashMap;
25 
26 public class HprofParser
27 {
28     private static final int STRING_IN_UTF8             =   0x01;
29     private static final int LOAD_CLASS                 =   0x02;
30     private static final int UNLOAD_CLASS               =   0x03;   //  unused
31     private static final int STACK_FRAME                =   0x04;
32     private static final int STACK_TRACE                =   0x05;
33     private static final int ALLOC_SITES                =   0x06;   //  unused
34     private static final int HEAP_SUMMARY               =   0x07;
35     private static final int START_THREAD               =   0x0a;   //  unused
36     private static final int END_THREAD                 =   0x0b;   //  unused
37     private static final int HEAP_DUMP                  =   0x0c;
38     private static final int HEAP_DUMP_SEGMENT          =   0x1c;
39     private static final int HEAP_DUMP_END              =   0x2c;
40     private static final int CPU_SAMPLES                =   0x0d;   //  unused
41     private static final int CONTROL_SETTINGS           =   0x0e;   //  unused
42 
43     private static final int ROOT_UNKNOWN               =   0xff;
44     private static final int ROOT_JNI_GLOBAL            =   0x01;
45     private static final int ROOT_JNI_LOCAL             =   0x02;
46     private static final int ROOT_JAVA_FRAME            =   0x03;
47     private static final int ROOT_NATIVE_STACK          =   0x04;
48     private static final int ROOT_STICKY_CLASS          =   0x05;
49     private static final int ROOT_THREAD_BLOCK          =   0x06;
50     private static final int ROOT_MONITOR_USED          =   0x07;
51     private static final int ROOT_THREAD_OBJECT         =   0x08;
52     private static final int ROOT_CLASS_DUMP            =   0x20;
53     private static final int ROOT_INSTANCE_DUMP         =   0x21;
54     private static final int ROOT_OBJECT_ARRAY_DUMP     =   0x22;
55     private static final int ROOT_PRIMITIVE_ARRAY_DUMP  =   0x23;
56 
57     /**
58      * Android format addition
59      *
60      * Specifies information about which heap certain objects came from.
61      * When a sub-tag of this type appears in a HPROF_HEAP_DUMP or
62      * HPROF_HEAP_DUMP_SEGMENT record, entries that follow it will be
63      * associated with the specified heap.  The HEAP_DUMP_INFO data is reset
64      * at the end of the HEAP_DUMP[_SEGMENT].  Multiple HEAP_DUMP_INFO entries
65      * may appear in a single HEAP_DUMP[_SEGMENT].
66      *
67      * Format:
68      *     u1: Tag value (0xFE)
69      *     u4: heap ID
70      *     ID: heap name string ID
71      */
72     private static final int ROOT_HEAP_DUMP_INFO        =   0xfe;
73     private static final int ROOT_INTERNED_STRING       =   0x89;
74     private static final int ROOT_FINALIZING            =   0x8a;
75     private static final int ROOT_DEBUGGER              =   0x8b;
76     private static final int ROOT_REFERENCE_CLEANUP     =   0x8c;
77     private static final int ROOT_VM_INTERNAL           =   0x8d;
78     private static final int ROOT_JNI_MONITOR           =   0x8e;
79     private static final int ROOT_UNREACHABLE           =   0x90;
80     private static final int ROOT_PRIMITIVE_ARRAY_NODATA=   0xc3;
81 
82     DataInputStream mInput;
83     int mIdSize;
84     State mState;
85 
86     byte[] mFieldBuffer = new byte[8];
87 
88     /*
89      * These are only needed while parsing so are not kept as part of the
90      * heap data.
91      */
92     HashMap<Long, String> mStrings = new HashMap<Long, String>();
93     HashMap<Long, String> mClassNames = new HashMap<Long, String>();
94 
HprofParser(DataInputStream in)95     public HprofParser(DataInputStream in) {
96         mInput = in;
97     }
98 
parse()99     public final State parse() {
100         State state = new State();
101         mState = state;
102 
103         try {
104             String  s = readNullTerminatedString();
105             DataInputStream in = mInput;
106 
107             mIdSize = in.readInt();
108             Types.setIdSize(mIdSize);
109 
110             in.readLong();  //  Timestamp, ignored for now
111 
112             while (true) {
113                 int tag = in.readUnsignedByte();
114                 int timestamp = in.readInt();
115                 int length = in.readInt();
116 
117                 switch (tag) {
118                     case STRING_IN_UTF8:
119                         loadString(length - 4);
120                         break;
121 
122                     case LOAD_CLASS:
123                         loadClass();
124                         break;
125 
126                     case STACK_FRAME:
127                         loadStackFrame();
128                         break;
129 
130                     case STACK_TRACE:
131                         loadStackTrace();
132                         break;
133 
134                     case HEAP_DUMP:
135                         loadHeapDump(length);
136                         mState.setToDefaultHeap();
137                         break;
138 
139                     case HEAP_DUMP_SEGMENT:
140                         loadHeapDump(length);
141                         mState.setToDefaultHeap();
142                         break;
143 
144                     default:
145                         skipFully(length);
146                 }
147 
148             }
149         } catch (EOFException eof) {
150             //  this is fine
151         } catch (Exception e) {
152             e.printStackTrace();
153         }
154 
155         mState.resolveReferences();
156 
157         return state;
158     }
159 
readNullTerminatedString()160     private String readNullTerminatedString() throws IOException {
161         StringBuilder s = new StringBuilder();
162         DataInputStream in = mInput;
163 
164         for (int c = in.read(); c != 0; c = in.read()) {
165             s.append((char) c);
166         }
167 
168         return s.toString();
169     }
170 
readId()171     private long readId() throws IOException {
172         switch (mIdSize) {
173             case 1: return mInput.readUnsignedByte();
174             case 2: return mInput.readUnsignedShort();
175             case 4: return ((long) mInput.readInt()) & 0x00000000ffffffffL;
176             case 8: return mInput.readLong();
177         }
178 
179         throw new IllegalArgumentException("ID Length must be 1, 2, 4, or 8");
180     }
181 
readUTF8(int length)182     private String readUTF8(int length) throws IOException {
183         byte[] b = new byte[length];
184 
185         mInput.read(b);
186 
187         return new String(b, "utf-8");
188     }
189 
loadString(int length)190     private void loadString(int length) throws IOException {
191         long id = readId();
192         String string = readUTF8(length);
193 
194         mStrings.put(id, string);
195     }
196 
loadClass()197     private void loadClass() throws IOException {
198         DataInputStream in = mInput;
199         int serial = in.readInt();
200         long id = readId();
201         int stackTrace = in.readInt();              //  unused
202         String name = mStrings.get(readId());
203 
204         mClassNames.put(id, name);
205     }
206 
loadStackFrame()207     private void loadStackFrame() throws IOException {
208         long id = readId();
209         String methodName = mStrings.get(readId());
210         String methodSignature = mStrings.get(readId());
211         String sourceFile = mStrings.get(readId());
212         int serial = mInput.readInt();
213         int lineNumber = mInput.readInt();
214 
215         StackFrame frame = new StackFrame(id, methodName, methodSignature,
216             sourceFile, serial, lineNumber);
217 
218         mState.addStackFrame(frame);
219     }
220 
loadStackTrace()221     private void loadStackTrace() throws IOException {
222         int serialNumber = mInput.readInt();
223         int threadSerialNumber = mInput.readInt();
224         final int numFrames = mInput.readInt();
225         StackFrame[] frames = new StackFrame[numFrames];
226 
227         for (int i = 0; i < numFrames; i++) {
228             frames[i] = mState.getStackFrame(readId());
229         }
230 
231         StackTrace trace = new StackTrace(serialNumber, threadSerialNumber,
232             frames);
233 
234         mState.addStackTrace(trace);
235     }
236 
loadHeapDump(int length)237     private void loadHeapDump(int length) throws IOException {
238         DataInputStream in = mInput;
239 
240         while (length > 0) {
241             int tag = in.readUnsignedByte();
242             length--;
243 
244             switch (tag) {
245                 case ROOT_UNKNOWN:
246                     length -= loadBasicObj(RootType.UNKNOWN);
247                     break;
248 
249                 case ROOT_JNI_GLOBAL:
250                     length -= loadBasicObj(RootType.NATIVE_STATIC);
251                     readId();   //  ignored
252                     length -= mIdSize;
253                     break;
254 
255                 case ROOT_JNI_LOCAL:
256                     length -= loadJniLocal();
257                     break;
258 
259                 case ROOT_JAVA_FRAME:
260                     length -= loadJavaFrame();
261                     break;
262 
263                 case ROOT_NATIVE_STACK:
264                     length -= loadNativeStack();
265                     break;
266 
267                 case ROOT_STICKY_CLASS:
268                     length -= loadBasicObj(RootType.SYSTEM_CLASS);
269                     break;
270 
271                 case ROOT_THREAD_BLOCK:
272                     length -= loadThreadBlock();
273                     break;
274 
275                 case ROOT_MONITOR_USED:
276                     length -= loadBasicObj(RootType.BUSY_MONITOR);
277                     break;
278 
279                 case ROOT_THREAD_OBJECT:
280                     length -= loadThreadObject();
281                     break;
282 
283                 case ROOT_CLASS_DUMP:
284                     length -= loadClassDump();
285                     break;
286 
287                 case ROOT_INSTANCE_DUMP:
288                     length -= loadInstanceDump();
289                     break;
290 
291                 case ROOT_OBJECT_ARRAY_DUMP:
292                     length -= loadObjectArrayDump();
293                     break;
294 
295                 case ROOT_PRIMITIVE_ARRAY_DUMP:
296                     length -= loadPrimitiveArrayDump();
297                     break;
298 
299                 case ROOT_PRIMITIVE_ARRAY_NODATA:
300                     System.err.println("+--- PRIMITIVE ARRAY NODATA DUMP");
301                     length -= loadPrimitiveArrayDump();
302 
303                     throw new IllegalArgumentException(
304                         "Don't know how to load a nodata array");
305 
306                 case ROOT_HEAP_DUMP_INFO:
307                     int heapId = mInput.readInt();
308                     long heapNameId = readId();
309                     String heapName = mStrings.get(heapNameId);
310 
311                     mState.setHeapTo(heapId, heapName);
312                     length -= 4 + mIdSize;
313                     break;
314 
315                 case ROOT_INTERNED_STRING:
316                     length -= loadBasicObj(RootType.INTERNED_STRING);
317                     break;
318 
319                 case ROOT_FINALIZING:
320                     length -= loadBasicObj(RootType.FINALIZING);
321                     break;
322 
323                 case ROOT_DEBUGGER:
324                     length -= loadBasicObj(RootType.DEBUGGER);
325                     break;
326 
327                 case ROOT_REFERENCE_CLEANUP:
328                     length -= loadBasicObj(RootType.REFERENCE_CLEANUP);
329                     break;
330 
331                 case ROOT_VM_INTERNAL:
332                     length -= loadBasicObj(RootType.VM_INTERNAL);
333                     break;
334 
335                 case ROOT_JNI_MONITOR:
336                     length -= loadJniMonitor();
337                     break;
338 
339                 case ROOT_UNREACHABLE:
340                     length -= loadBasicObj(RootType.UNREACHABLE);
341                     break;
342 
343                 default:
344                     throw new IllegalArgumentException(
345                         "loadHeapDump loop with unknown tag " + tag
346                         + " with " + mInput.available()
347                         + " bytes possibly remaining");
348             }
349         }
350     }
351 
loadJniLocal()352     private int loadJniLocal() throws IOException {
353         long id = readId();
354         int threadSerialNumber = mInput.readInt();
355         int stackFrameNumber = mInput.readInt();
356         ThreadObj thread = mState.getThread(threadSerialNumber);
357         StackTrace trace = mState.getStackTraceAtDepth(thread.mStackTrace,
358             stackFrameNumber);
359         RootObj root = new RootObj(RootType.NATIVE_LOCAL, id,
360             threadSerialNumber, trace);
361 
362         root.setHeap(mState.mCurrentHeap);
363         mState.addRoot(root);
364 
365         return mIdSize + 4 + 4;
366     }
367 
loadJavaFrame()368     private int loadJavaFrame() throws IOException {
369         long id = readId();
370         int threadSerialNumber = mInput.readInt();
371         int stackFrameNumber = mInput.readInt();
372         ThreadObj thread = mState.getThread(threadSerialNumber);
373         StackTrace trace = mState.getStackTraceAtDepth(thread.mStackTrace,
374             stackFrameNumber);
375         RootObj root = new RootObj(RootType.JAVA_LOCAL, id, threadSerialNumber,
376             trace);
377 
378         root.setHeap(mState.mCurrentHeap);
379         mState.addRoot(root);
380 
381         return mIdSize + 4 + 4;
382     }
383 
loadNativeStack()384     private int loadNativeStack() throws IOException {
385         long id = readId();
386         int threadSerialNumber = mInput.readInt();
387         ThreadObj thread = mState.getThread(threadSerialNumber);
388         StackTrace trace = mState.getStackTrace(thread.mStackTrace);
389         RootObj root = new RootObj(RootType.NATIVE_STACK, id,
390             threadSerialNumber, trace);
391 
392         root.setHeap(mState.mCurrentHeap);
393         mState.addRoot(root);
394 
395         return mIdSize + 4;
396     }
397 
loadBasicObj(RootType type)398     private int loadBasicObj(RootType type) throws IOException {
399         long id = readId();
400         RootObj root = new RootObj(type, id);
401 
402         root.setHeap(mState.mCurrentHeap);
403         mState.addRoot(root);
404 
405         return mIdSize;
406     }
407 
loadThreadBlock()408     private int loadThreadBlock() throws IOException {
409         long id = readId();
410         int threadSerialNumber = mInput.readInt();
411         ThreadObj thread = mState.getThread(threadSerialNumber);
412         StackTrace stack = mState.getStackTrace(thread.mStackTrace);
413         RootObj root = new RootObj(RootType.THREAD_BLOCK, id,
414             threadSerialNumber, stack);
415 
416         root.setHeap(mState.mCurrentHeap);
417         mState.addRoot(root);
418 
419         return mIdSize + 4;
420     }
421 
loadThreadObject()422     private int loadThreadObject() throws IOException {
423         long id = readId();
424         int threadSerialNumber = mInput.readInt();
425         int stackSerialNumber = mInput.readInt();
426         ThreadObj thread = new ThreadObj(id, stackSerialNumber);
427 
428         mState.addThread(thread, threadSerialNumber);
429 
430         return mIdSize + 4 + 4;
431     }
432 
loadClassDump()433     private int loadClassDump() throws IOException {
434         int bytesRead = 0;
435         DataInputStream in = mInput;
436         long id = readId();
437         int stackSerialNumber = in.readInt();
438         StackTrace stack = mState.getStackTrace(stackSerialNumber);
439         long superClassId = readId();
440         long classLoaderId = readId();
441         long signersId = readId();
442         long protectionDomainId = readId();
443         long reserved1 = readId();
444         long reserved2 = readId();
445         int instanceSize = in.readInt();
446 
447         bytesRead = (7 * mIdSize) + 4 + 4;
448 
449         //  Skip over the constant pool
450         int numEntries = in.readUnsignedShort();
451         bytesRead += 2;
452 
453         for (int i = 0; i < numEntries; i++) {
454             in.readUnsignedShort();
455             bytesRead += 2 + skipValue();
456         }
457 
458         //  Static fields
459         numEntries = in.readUnsignedShort();
460         bytesRead += 2;
461 
462         String[] staticFieldNames = new String[numEntries];
463         int[] staticFieldTypes = new int[numEntries];
464         ByteArrayOutputStream staticFieldValues = new ByteArrayOutputStream();
465         byte[] buffer = mFieldBuffer;
466 
467         for (int i = 0; i < numEntries; i++) {
468             staticFieldNames[i] = mStrings.get(readId());
469 
470             int fieldType = in.readByte();
471             int fieldSize = Types.getTypeSize(fieldType);
472             staticFieldTypes[i] = fieldType;
473 
474             in.readFully(buffer, 0, fieldSize);
475             staticFieldValues.write(buffer, 0, fieldSize);
476 
477             bytesRead += mIdSize + 1 + fieldSize;
478         }
479 
480         //  Instance fields
481         numEntries = in.readUnsignedShort();
482         bytesRead += 2;
483 
484         String[] names = new String[numEntries];
485         int[] types = new int[numEntries];
486 
487         for (int i = 0; i < numEntries; i++) {
488             long fieldName = readId();
489             int type = in.readUnsignedByte();
490 
491             names[i] = mStrings.get(fieldName);
492             types[i] = type;
493 
494             bytesRead += mIdSize + 1;
495         }
496 
497         ClassObj theClass = new ClassObj(id, stack, mClassNames.get(id));
498 
499         theClass.setStaticFieldNames(staticFieldNames);
500         theClass.setStaticFieldTypes(staticFieldTypes);
501         theClass.setStaticFieldValues(staticFieldValues.toByteArray());
502 
503         theClass.setSuperclassId(superClassId);
504         theClass.setFieldNames(names);
505         theClass.setFieldTypes(types);
506         theClass.setSize(instanceSize);
507 
508         theClass.setHeap(mState.mCurrentHeap);
509 
510         mState.addClass(id, theClass);
511 
512         return bytesRead;
513     }
514 
loadInstanceDump()515     private int loadInstanceDump() throws IOException {
516         long id = readId();
517         int stackId = mInput.readInt();
518         StackTrace stack = mState.getStackTrace(stackId);
519         long classId = readId();
520         int remaining = mInput.readInt();
521         ClassInstance instance = new ClassInstance(id, stack, classId);
522 
523         instance.loadFieldData(mInput, remaining);
524         instance.setHeap(mState.mCurrentHeap);
525         mState.addInstance(id, instance);
526 
527         return mIdSize + 4 + mIdSize + 4 + remaining;
528     }
529 
loadObjectArrayDump()530     private int loadObjectArrayDump() throws IOException {
531         long id = readId();
532         int stackId = mInput.readInt();
533         StackTrace stack = mState.getStackTrace(stackId);
534         int numElements = mInput.readInt();
535         long classId = readId();
536         int totalBytes = numElements * mIdSize;
537         byte[] data = new byte[totalBytes];
538         String className = mClassNames.get(classId);
539 
540         mInput.readFully(data);
541 
542         ArrayInstance array = new ArrayInstance(id, stack, Types.OBJECT,
543             numElements, data);
544 
545         array.mClassId = classId;
546         array.setHeap(mState.mCurrentHeap);
547         mState.addInstance(id, array);
548 
549         return mIdSize + 4 + 4 + mIdSize + totalBytes;
550     }
551 
loadPrimitiveArrayDump()552     private int loadPrimitiveArrayDump() throws IOException {
553         long id = readId();
554         int stackId = mInput.readInt();
555         StackTrace stack = mState.getStackTrace(stackId);
556         int numElements = mInput.readInt();
557         int type = mInput.readUnsignedByte();
558         int size = Types.getTypeSize(type);
559         int totalBytes = numElements * size;
560         byte[] data = new byte[totalBytes];
561 
562         mInput.readFully(data);
563 
564         ArrayInstance array = new ArrayInstance(id, stack, type, numElements,
565             data);
566 
567         array.setHeap(mState.mCurrentHeap);
568         mState.addInstance(id, array);
569 
570         return mIdSize + 4 + 4 + 1 + totalBytes;
571     }
572 
loadJniMonitor()573     private int loadJniMonitor() throws IOException {
574         long id = readId();
575         int threadSerialNumber = mInput.readInt();
576         int stackDepth = mInput.readInt();
577         ThreadObj thread = mState.getThread(threadSerialNumber);
578         StackTrace trace = mState.getStackTraceAtDepth(thread.mStackTrace,
579             stackDepth);
580         RootObj root = new RootObj(RootType.NATIVE_MONITOR, id,
581             threadSerialNumber, trace);
582 
583         root.setHeap(mState.mCurrentHeap);
584         mState.addRoot(root);
585 
586         return mIdSize + 4 + 4;
587     }
588 
skipValue()589     private int skipValue() throws IOException {
590         int type = mInput.readUnsignedByte();
591         int size = Types.getTypeSize(type);
592 
593         skipFully(size);
594 
595         return size + 1;
596     }
597 
598     /*
599      * BufferedInputStream will not skip(int) the entire requested number
600      * of bytes if it extends past the current buffer boundary.  So, this
601      * routine is needed to actually skip over the requested number of bytes
602      * using as many iterations as needed.
603      */
skipFully(long numBytes)604     private void skipFully(long numBytes) throws IOException {
605         while (numBytes > 0) {
606             long skipped = mInput.skip(numBytes);
607 
608             numBytes -= skipped;
609         }
610     }
611 }
612