1# Copyright (C) 2018 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15# usage: python hprofdump.py FILE 16# Dumps a binary heap dump file to text, to facilitate debugging of heap 17# dumps and heap dump viewers. 18 19import time 20import struct 21import sys 22 23filename = sys.argv[1] 24hprof = open(filename, "rb") 25 26def readu1(hprof): 27 return struct.unpack('!B', hprof.read(1))[0] 28 29def readu2(hprof): 30 return struct.unpack('!H', hprof.read(2))[0] 31 32def readu4(hprof): 33 return struct.unpack('!I', hprof.read(4))[0] 34 35def readu8(hprof): 36 return struct.unpack('!Q', hprof.read(8))[0] 37 38def readN(n, hprof): 39 if n == 1: 40 return readu1(hprof) 41 if n == 2: 42 return readu2(hprof) 43 if n == 4: 44 return readu4(hprof) 45 if n == 8: 46 return readu8(hprof) 47 raise Exception("Unsupported size of readN: %d" % n) 48 49TY_OBJECT = 2 50TY_BOOLEAN = 4 51TY_CHAR = 5 52TY_FLOAT = 6 53TY_DOUBLE = 7 54TY_BYTE = 8 55TY_SHORT = 9 56TY_INT = 10 57TY_LONG = 11 58 59def showty(ty): 60 if ty == TY_OBJECT: 61 return "Object" 62 if ty == TY_BOOLEAN: 63 return "boolean" 64 if ty == TY_CHAR: 65 return "char" 66 if ty == TY_FLOAT: 67 return "float" 68 if ty == TY_DOUBLE: 69 return "double" 70 if ty == TY_BYTE: 71 return "byte" 72 if ty == TY_SHORT: 73 return "short" 74 if ty == TY_INT: 75 return "int" 76 if ty == TY_LONG: 77 return "long" 78 raise Exception("Unsupported type %d" % ty) 79 80strs = { } 81def showstr(id): 82 if id in strs: 83 return strs[id] 84 return "STR[@%x]" % id 85 86loaded = { } 87def showloaded(serial): 88 if serial in loaded: 89 return showstr(loaded[serial]) 90 return "SERIAL[@%x]" % serial 91 92classobjs = { } 93def showclassobj(id): 94 if id in classobjs: 95 return "%s @%x" % (showstr(classobjs[id]), id) 96 return "@%x" % id 97 98 99# [u1]* An initial NULL terminate series of bytes representing the format name 100# and version. 101version = "" 102c = hprof.read(1) 103while (c != '\0'): 104 version += c 105 c = hprof.read(1) 106print "Version: %s" % version 107 108# [u4] size of identifiers. 109idsize = readu4(hprof) 110print "ID Size: %d bytes" % idsize 111def readID(hprof): 112 return readN(idsize, hprof) 113 114def valsize(ty): 115 if ty == TY_OBJECT: 116 return idsize 117 if ty == TY_BOOLEAN: 118 return 1 119 if ty == TY_CHAR: 120 return 2 121 if ty == TY_FLOAT: 122 return 4 123 if ty == TY_DOUBLE: 124 return 8 125 if ty == TY_BYTE: 126 return 1 127 if ty == TY_SHORT: 128 return 2 129 if ty == TY_INT: 130 return 4 131 if ty == TY_LONG: 132 return 8 133 raise Exception("Unsupported type %d" % ty) 134 135def readval(ty, hprof): 136 return readN(valsize(ty), hprof) 137 138# [u4] high word of number of ms since 0:00 GMT, 1/1/70 139# [u4] low word of number of ms since 0:00 GMT, 1/1/70 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 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" % (id, showstr(method), showstr(sig), showstr(file), line, serial) 171 elif tag == 0x05: 172 serial = readu4(hprof) 173 print "STACK TRACE %d" % serial 174 thread = readu4(hprof) 175 frames = readu4(hprof) 176 hprof.read(idsize * frames) 177 elif tag == 0x06: 178 print "ALLOC SITES" 179 flags = readu2(hprof) 180 cutoff_ratio = readu4(hprof) 181 live_bytes = readu4(hprof) 182 live_insts = readu4(hprof) 183 alloc_bytes = readu8(hprof) 184 alloc_insts = readu8(hprof) 185 numsites = readu4(hprof) 186 while numsites > 0: 187 indicator = readu1(hprof) 188 class_serial = readu4(hprof) 189 stack = readu4(hprof) 190 live_bytes = readu4(hprof) 191 live_insts = readu4(hprof) 192 alloc_bytes = readu4(hprof) 193 alloc_insts = readu4(hprof) 194 numsites -= 1 195 elif tag == 0x0A: 196 thread = readu4(hprof) 197 object = readID(hprof) 198 stack = readu4(hprof) 199 name = readID(hprof) 200 group_name = readID(hprof) 201 pgroup_name = readID(hprof) 202 print "START THREAD serial=%d" % thread 203 elif tag == 0x0B: 204 thread = readu4(hprof) 205 print "END THREAD" 206 elif tag == 0x0C or tag == 0x1C: 207 if tag == 0x0C: 208 print "HEAP DUMP" 209 else: 210 print "HEAP DUMP SEGMENT" 211 212 while (length > 0): 213 subtag = readu1(hprof) ; length -= 1 214 if subtag == 0xFF: 215 print " ROOT UNKNOWN" 216 objid = readID(hprof) ; length -= idsize 217 elif subtag == 0x01: 218 print " ROOT JNI GLOBAL" 219 objid = readID(hprof) ; length -= idsize 220 ref = readID(hprof) ; length -= idsize 221 elif subtag == 0x02: 222 print " ROOT JNI LOCAL" 223 objid = readID(hprof) ; length -= idsize 224 thread = readu4(hprof) ; length -= 4 225 frame = readu4(hprof) ; length -= 4 226 elif subtag == 0x03: 227 print " ROOT JAVA FRAME" 228 objid = readID(hprof) ; length -= idsize 229 serial = readu4(hprof) ; length -= 4 230 frame = readu4(hprof) ; length -= 4 231 elif subtag == 0x04: 232 objid = readID(hprof) ; length -= idsize 233 serial = readu4(hprof) ; length -= 4 234 print " ROOT NATIVE STACK serial=%d" % serial 235 elif subtag == 0x05: 236 print " ROOT STICKY CLASS" 237 objid = readID(hprof) ; length -= idsize 238 elif subtag == 0x06: 239 print " ROOT THREAD BLOCK" 240 objid = readID(hprof) ; length -= idsize 241 thread = readu4(hprof) ; length -= 4 242 elif subtag == 0x07: 243 print " ROOT MONITOR USED" 244 objid = readID(hprof) ; length -= idsize 245 elif subtag == 0x08: 246 threadid = readID(hprof) ; length -= idsize 247 serial = readu4(hprof) ; length -= 4 248 stack = readu4(hprof) ; length -= 4 249 print " ROOT THREAD OBJECT threadid=@%x serial=%d" % (threadid, serial) 250 elif subtag == 0x20: 251 print " CLASS DUMP" 252 print " class class object ID: %s" % showclassobj(readID(hprof)) ; length -= idsize 253 print " stack trace serial number: #%d" % readu4(hprof) ; length -= 4 254 print " super class object ID: @%x" % readID(hprof) ; length -= idsize 255 print " class loader object ID: @%x" % readID(hprof) ; length -= idsize 256 print " signers object ID: @%x" % readID(hprof) ; length -= idsize 257 print " protection domain object ID: @%x" % readID(hprof) ; length -= idsize 258 print " reserved: @%x" % readID(hprof) ; length -= idsize 259 print " reserved: @%x" % readID(hprof) ; length -= idsize 260 print " instance size (in bytes): %d" % readu4(hprof) ; length -= 4 261 print " constant pool:" 262 poolsize = readu2(hprof) ; length -= 2 263 while poolsize > 0: 264 poolsize -= 1 265 idx = readu2(hprof) ; length -= 2 266 ty = readu1(hprof) ; length -= 1 267 val = readval(ty, hprof) ; length -= valsize(ty) 268 print " %d %s 0x%x" % (idx, showty(ty), val) 269 numstatic = readu2(hprof) ; length -= 2 270 print " static fields:" 271 while numstatic > 0: 272 numstatic -= 1 273 nameid = readID(hprof) ; length -= idsize 274 ty = readu1(hprof) ; length -= 1 275 val = readval(ty, hprof) ; length -= valsize(ty) 276 print " %s %s 0x%x" % (showstr(nameid), showty(ty), val) 277 numinst = readu2(hprof) ; length -= 2 278 print " instance fields:" 279 while numinst > 0: 280 numinst -= 1 281 nameid = readID(hprof) ; length -= idsize 282 ty = readu1(hprof) ; length -= 1 283 print " %s %s" % (showstr(nameid), showty(ty)) 284 elif subtag == 0x21: 285 print " INSTANCE DUMP:" 286 print " object ID: @%x" % readID(hprof) ; length -= idsize 287 stack = readu4(hprof) ; length -= 4 288 print " stack: %s" % stack 289 print " class object ID: %s" % showclassobj(readID(hprof)) ; length -= idsize 290 datalen = readu4(hprof) ; length -= 4 291 print " %d bytes of instance data" % datalen 292 data = hprof.read(datalen) ; length -= datalen 293 elif subtag == 0x22: 294 print " OBJECT ARRAY DUMP:" 295 print " array object ID: @%x" % readID(hprof) ; length -= idsize 296 stack = readu4(hprof) ; length -= 4 297 print " stack: %s" % stack 298 count = readu4(hprof) ; length -= 4 299 print " array class object ID: %s" % showclassobj(readID(hprof)) ; length -= idsize 300 hprof.read(idsize * count) ; length -= (idsize * count) 301 elif subtag == 0x23: 302 print " PRIMITIVE ARRAY DUMP:" 303 print " array object ID: @%x" % readID(hprof) ; length -= idsize 304 stack = readu4(hprof) ; length -= 4 305 count = readu4(hprof) ; length -= 4 306 ty = readu1(hprof) ; length -= 1 307 hprof.read(valsize(ty)*count) ; length -= (valsize(ty)*count) 308 elif subtag == 0x89: 309 print " HPROF_ROOT_INTERNED_STRING" 310 objid = readID(hprof) ; length -= idsize 311 elif subtag == 0x8b: 312 objid = readID(hprof) ; length -= idsize 313 print " HPROF ROOT DEBUGGER @%x (at offset %d)" % (objid, hprof.tell() - (idsize + 1)) 314 elif subtag == 0x8d: 315 objid = readID(hprof) ; length -= idsize 316 print " HPROF ROOT VM INTERNAL @%x" % objid 317 elif subtag == 0xfe: 318 hty = readu4(hprof) ; length -= 4 319 hnameid = readID(hprof) ; length -= idsize 320 print " HPROF_HEAP_DUMP_INFO %s" % showstr(hnameid) 321 else: 322 raise Exception("TODO: subtag %x" % subtag) 323 elif tag == 0x0E: 324 flags = readu4(hprof) 325 depth = readu2(hprof) 326 print "CONTROL SETTINGS %x %d" % (flags, depth) 327 elif tag == 0x2C: 328 print "HEAP DUMP END" 329 else: 330 raise Exception("TODO: TAG %x" % tag) 331 332