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