• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Lint as: python2, python3
2# Copyright (C) 2018 The Android Open Source Project
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# usage: python hprofdump.py FILE
17#   Dumps a binary heap dump file to text, to facilitate debugging of heap
18#   dumps and heap dump viewers.
19
20import time
21
22import struct
23import sys
24
25filename = sys.argv[1]
26hprof = open(filename, "rb")
27
28def readu1(hprof):
29    return struct.unpack('!B', hprof.read(1))[0]
30
31def readu2(hprof):
32    return struct.unpack('!H', hprof.read(2))[0]
33
34def readu4(hprof):
35    return struct.unpack('!I', hprof.read(4))[0]
36
37def readu8(hprof):
38    return struct.unpack('!Q', hprof.read(8))[0]
39
40def readN(n, hprof):
41    if n == 1:
42        return readu1(hprof)
43    if n == 2:
44        return readu2(hprof)
45    if n == 4:
46        return readu4(hprof)
47    if n == 8:
48        return readu8(hprof)
49    raise Exception("Unsupported size of readN: %d" % n)
50
51TY_OBJECT = 2
52TY_BOOLEAN = 4
53TY_CHAR = 5
54TY_FLOAT = 6
55TY_DOUBLE = 7
56TY_BYTE = 8
57TY_SHORT = 9
58TY_INT = 10
59TY_LONG = 11
60
61def showty(ty):
62    if ty == TY_OBJECT:
63        return "Object"
64    if ty == TY_BOOLEAN:
65        return "boolean"
66    if ty == TY_CHAR:
67        return "char"
68    if ty == TY_FLOAT:
69        return "float"
70    if ty == TY_DOUBLE:
71        return "double"
72    if ty == TY_BYTE:
73        return "byte"
74    if ty == TY_SHORT:
75        return "short"
76    if ty == TY_INT:
77        return "int"
78    if ty == TY_LONG:
79        return "long"
80    raise Exception("Unsupported type %d" % ty)
81
82strs = {}
83
84def showstr(id):
85    if id in strs:
86        return strs[id]
87    return "STR[@%x]" % id
88
89loaded = {}
90
91def showloaded(serial):
92    if serial in loaded:
93        return showstr(loaded[serial])
94    return "SERIAL[@%x]" % serial
95
96classobjs = {}
97
98def showclassobj(id):
99    if id in classobjs:
100        return "%s @%x" % (showstr(classobjs[id]), id)
101    return "@%x" % id
102
103version = ""
104c = hprof.read(1)
105while (c != b'\0'):
106    version += c.decode('ascii')
107    c = hprof.read(1)
108print("Version: %s" % version)
109
110idsize = readu4(hprof)
111print("ID Size: %d bytes" % idsize)
112
113def readID(hprof):
114    return readN(idsize, hprof)
115
116def valsize(ty):
117    if ty == TY_OBJECT:
118        return idsize
119    if ty == TY_BOOLEAN:
120        return 1
121    if ty == TY_CHAR:
122        return 2
123    if ty == TY_FLOAT:
124        return 4
125    if ty == TY_DOUBLE:
126        return 8
127    if ty == TY_BYTE:
128        return 1
129    if ty == TY_SHORT:
130        return 2
131    if ty == TY_INT:
132        return 4
133    if ty == TY_LONG:
134        return 8
135    raise Exception("Unsupported type %d" % ty)
136
137def readval(ty, hprof):
138    return readN(valsize(ty), hprof)
139
140timestamp = (readu4(hprof) << 32) | readu4(hprof)
141s, ms = divmod(timestamp, 1000)
142print("Date: %s.%03d" % (time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(s)), ms))
143
144while hprof.read(1):
145    hprof.seek(-1, 1)
146    pos = hprof.tell()
147    tag = readu1(hprof)
148    time = readu4(hprof)
149    length = readu4(hprof)
150    if tag == 0x01:
151        id = readID(hprof)
152        string = hprof.read(length - idsize)
153        print("%d: STRING %x %s" % (pos, id, repr(string)))
154        strs[id] = string.decode('latin-1')
155    elif tag == 0x02:
156        serial = readu4(hprof)
157        classobj = readID(hprof)
158        stack = readu4(hprof)
159        classname = readID(hprof)
160        loaded[serial] = classname
161        classobjs[classobj] = classname
162        print("LOAD CLASS #%d %s @%x stack=@%x" % (serial, showstr(classname), classobj, stack))
163    elif tag == 0x04:
164        id = readID(hprof)
165        method = readID(hprof)
166        sig = readID(hprof)
167        file = readID(hprof)
168        serial = readu4(hprof)
169        line = readu4(hprof)
170        print("STACK FRAME %d '%s' '%s' '%s' line=%d classserial=%d" % (
171        id, showstr(method), showstr(sig), showstr(file), line, serial))
172    elif tag == 0x05:
173        serial = readu4(hprof)
174        print("STACK TRACE %d" % serial)
175        thread = readu4(hprof)
176        frames = readu4(hprof)
177        hprof.read(idsize * frames)
178    elif tag == 0x06:
179        print("ALLOC SITES")
180        flags = readu2(hprof)
181        cutoff_ratio = readu4(hprof)
182        live_bytes = readu4(hprof)
183        live_insts = readu4(hprof)
184        alloc_bytes = readu8(hprof)
185        alloc_insts = readu8(hprof)
186        numsites = readu4(hprof)
187        while numsites > 0:
188            indicator = readu1(hprof)
189            class_serial = readu4(hprof)
190            stack = readu4(hprof)
191            live_bytes = readu4(hprof)
192            live_insts = readu4(hprof)
193            alloc_bytes = readu4(hprof)
194            alloc_insts = readu4(hprof)
195            numsites -= 1
196    elif tag == 0x0A:
197        thread = readu4(hprof)
198        object = readID(hprof)
199        stack = readu4(hprof)
200        name = readID(hprof)
201        group_name = readID(hprof)
202        pgroup_name = readID(hprof)
203        print("START THREAD serial=%d" % thread)
204    elif tag == 0x0B:
205        thread = readu4(hprof)
206        print("END THREAD")
207    elif tag == 0x0C or tag == 0x1C:
208        if tag == 0x0C:
209            print("HEAP DUMP")
210        else:
211            print("HEAP DUMP SEGMENT")
212
213        while (length > 0):
214            subtag = readu1(hprof);
215            length -= 1
216            if subtag == 0xFF:
217                print(" ROOT UNKNOWN")
218                objid = readID(hprof);
219                length -= idsize
220            elif subtag == 0x01:
221                print(" ROOT JNI GLOBAL")
222                objid = readID(hprof);
223                length -= idsize
224                ref = readID(hprof);
225                length -= idsize
226            elif subtag == 0x02:
227                print(" ROOT JNI LOCAL")
228                objid = readID(hprof);
229                length -= idsize
230                thread = readu4(hprof);
231                length -= 4
232                frame = readu4(hprof);
233                length -= 4
234            elif subtag == 0x03:
235                print(" ROOT JAVA FRAME")
236                objid = readID(hprof);
237                length -= idsize
238                serial = readu4(hprof);
239                length -= 4
240                frame = readu4(hprof);
241                length -= 4
242            elif subtag == 0x04:
243                objid = readID(hprof);
244                length -= idsize
245                serial = readu4(hprof);
246                length -= 4
247                print(" ROOT NATIVE STACK serial=%d" % serial)
248            elif subtag == 0x05:
249                print(" ROOT STICKY CLASS")
250                objid = readID(hprof);
251                length -= idsize
252            elif subtag == 0x06:
253                print(" ROOT THREAD BLOCK")
254                objid = readID(hprof);
255                length -= idsize
256                thread = readu4(hprof);
257                length -= 4
258            elif subtag == 0x07:
259                print(" ROOT MONITOR USED")
260                objid = readID(hprof);
261                length -= idsize
262            elif subtag == 0x08:
263                threadid = readID(hprof);
264                length -= idsize
265                serial = readu4(hprof);
266                length -= 4
267                stack = readu4(hprof);
268                length -= 4
269                print(" ROOT THREAD OBJECT threadid=@%x serial=%d" % (threadid, serial))
270            elif subtag == 0x20:
271                print(" CLASS DUMP")
272                print("  class class object ID: %s" % showclassobj(readID(hprof)));
273                length -= idsize
274                print("  stack trace serial number: #%d" % readu4(hprof));
275                length -= 4
276                print("  super class object ID: @%x" % readID(hprof));
277                length -= idsize
278                print("  class loader object ID: @%x" % readID(hprof));
279                length -= idsize
280                print("  signers object ID: @%x" % readID(hprof));
281                length -= idsize
282                print("  protection domain object ID: @%x" % readID(hprof));
283                length -= idsize
284                print("  reserved: @%x" % readID(hprof));
285                length -= idsize
286                print("  reserved: @%x" % readID(hprof));
287                length -= idsize
288                print("  instance size (in bytes): %d" % readu4(hprof));
289                length -= 4
290                print("  constant pool:")
291                poolsize = readu2(hprof);
292                length -= 2
293                while poolsize > 0:
294                    poolsize -= 1
295                    idx = readu2(hprof);
296                    length -= 2
297                    ty = readu1(hprof);
298                    length -= 1
299                    val = readval(ty, hprof);
300                    length -= valsize(ty)
301                    print("   %d %s 0x%x" % (idx, showty(ty), val))
302                numstatic = readu2(hprof);
303                length -= 2
304                print("  static fields:")
305                while numstatic > 0:
306                    numstatic -= 1
307                    nameid = readID(hprof);
308                    length -= idsize
309                    ty = readu1(hprof);
310                    length -= 1
311                    val = readval(ty, hprof);
312                    length -= valsize(ty)
313                    print("   %s %s 0x%x" % (showstr(nameid), showty(ty), val))
314                numinst = readu2(hprof);
315                length -= 2
316                print("  instance fields:")
317                while numinst > 0:
318                    numinst -= 1
319                    nameid = readID(hprof);
320                    length -= idsize
321                    ty = readu1(hprof);
322                    length -= 1
323                    print("   %s %s" % (showstr(nameid), showty(ty)))
324            elif subtag == 0x21:
325                print(" INSTANCE DUMP:")
326                print("  object ID: @%x" % readID(hprof));
327                length -= idsize
328                stack = readu4(hprof);
329                length -= 4
330                print("  stack: %s" % stack)
331                print("  class object ID: %s" % showclassobj(readID(hprof)));
332                length -= idsize
333                datalen = readu4(hprof);
334                length -= 4
335                print("  %d bytes of instance data" % datalen)
336                data = hprof.read(datalen);
337                length -= datalen
338            elif subtag == 0x22:
339                print(" OBJECT ARRAY DUMP:")
340                print("  array object ID: @%x" % readID(hprof));
341                length -= idsize
342                stack = readu4(hprof);
343                length -= 4
344                print("  stack: %s" % stack)
345                count = readu4(hprof);
346                length -= 4
347                print("  array class object ID: %s" % showclassobj(readID(hprof)));
348                length -= idsize
349                hprof.read(idsize * count);
350                length -= (idsize * count)
351            elif subtag == 0x23:
352                print(" PRIMITIVE ARRAY DUMP:")
353                print("  array object ID: @%x" % readID(hprof));
354                length -= idsize
355                stack = readu4(hprof);
356                length -= 4
357                count = readu4(hprof);
358                length -= 4
359                ty = readu1(hprof);
360                length -= 1
361                hprof.read(valsize(ty) * count);
362                length -= (valsize(ty) * count)
363            elif subtag == 0x89:
364                print(" HPROF_ROOT_INTERNED_STRING")
365                objid = readID(hprof);
366                length -= idsize
367            elif subtag == 0x8b:
368                objid = readID(hprof);
369                length -= idsize
370                print(" HPROF ROOT DEBUGGER @%x (at offset %d)" % (objid, hprof.tell() - (idsize + 1)))
371            elif subtag == 0x8d:
372                objid = readID(hprof);
373                length -= idsize
374                print(" HPROF ROOT VM INTERNAL @%x" % objid)
375            elif subtag == 0xfe:
376                hty = readu4(hprof);
377                length -= 4
378                hnameid = readID(hprof);
379                length -= idsize
380                print(" HPROF_HEAP_DUMP_INFO %s" % showstr(hnameid))
381            else:
382                raise Exception("TODO: subtag %x" % subtag)
383    elif tag == 0x0E:
384        flags = readu4(hprof)
385        depth = readu2(hprof)
386        print("CONTROL SETTINGS %x %d" % (flags, depth))
387    elif tag == 0x2C:
388        print("HEAP DUMP END")
389    else:
390        raise Exception("TODO: TAG %x" % tag)
391