1 #!/usr/bin/env python 2 # 3 # Copyright 2012 the V8 project authors. All rights reserved. 4 # Redistribution and use in source and binary forms, with or without 5 # modification, are permitted provided that the following conditions are 6 # met: 7 # 8 # * Redistributions of source code must retain the above copyright 9 # notice, this list of conditions and the following disclaimer. 10 # * Redistributions in binary form must reproduce the above 11 # copyright notice, this list of conditions and the following 12 # disclaimer in the documentation and/or other materials provided 13 # with the distribution. 14 # * Neither the name of Google Inc. nor the names of its 15 # contributors may be used to endorse or promote products derived 16 # from this software without specific prior written permission. 17 # 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30 import bisect 31 import cmd 32 import codecs 33 import ctypes 34 import datetime 35 import disasm 36 import mmap 37 import optparse 38 import os 39 import re 40 import struct 41 import sys 42 import types 43 import v8heapconst 44 45 USAGE="""usage: %prog [OPTIONS] [DUMP-FILE] 46 47 Minidump analyzer. 48 49 Shows the processor state at the point of exception including the 50 stack of the active thread and the referenced objects in the V8 51 heap. Code objects are disassembled and the addresses linked from the 52 stack (e.g. pushed return addresses) are marked with "=>". 53 54 Examples: 55 $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp""" 56 57 58 DEBUG=False 59 60 61 def DebugPrint(s): 62 if not DEBUG: return 63 print s 64 65 66 class Descriptor(object): 67 """Descriptor of a structure in a memory.""" 68 69 def __init__(self, fields): 70 self.fields = fields 71 self.is_flexible = False 72 for _, type_or_func in fields: 73 if isinstance(type_or_func, types.FunctionType): 74 self.is_flexible = True 75 break 76 if not self.is_flexible: 77 self.ctype = Descriptor._GetCtype(fields) 78 self.size = ctypes.sizeof(self.ctype) 79 80 def Read(self, memory, offset): 81 if self.is_flexible: 82 fields_copy = self.fields[:] 83 last = 0 84 for name, type_or_func in fields_copy: 85 if isinstance(type_or_func, types.FunctionType): 86 partial_ctype = Descriptor._GetCtype(fields_copy[:last]) 87 partial_object = partial_ctype.from_buffer(memory, offset) 88 type = type_or_func(partial_object) 89 if type is not None: 90 fields_copy[last] = (name, type) 91 last += 1 92 else: 93 last += 1 94 complete_ctype = Descriptor._GetCtype(fields_copy[:last]) 95 else: 96 complete_ctype = self.ctype 97 return complete_ctype.from_buffer(memory, offset) 98 99 @staticmethod 100 def _GetCtype(fields): 101 class Raw(ctypes.Structure): 102 _fields_ = fields 103 _pack_ = 1 104 105 def __str__(self): 106 return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field)) 107 for field, _ in Raw._fields_) + "}" 108 return Raw 109 110 111 def FullDump(reader, heap): 112 """Dump all available memory regions.""" 113 def dump_region(reader, start, size, location): 114 print 115 while start & 3 != 0: 116 start += 1 117 size -= 1 118 location += 1 119 is_executable = reader.IsProbableExecutableRegion(location, size) 120 is_ascii = reader.IsProbableASCIIRegion(location, size) 121 122 if is_executable is not False: 123 lines = reader.GetDisasmLines(start, size) 124 for line in lines: 125 print FormatDisasmLine(start, heap, line) 126 print 127 128 if is_ascii is not False: 129 # Output in the same format as the Unix hd command 130 addr = start 131 for slot in xrange(location, location + size, 16): 132 hex_line = "" 133 asc_line = "" 134 for i in xrange(0, 16): 135 if slot + i < location + size: 136 byte = ctypes.c_uint8.from_buffer(reader.minidump, slot + i).value 137 if byte >= 0x20 and byte < 0x7f: 138 asc_line += chr(byte) 139 else: 140 asc_line += "." 141 hex_line += " %02x" % (byte) 142 else: 143 hex_line += " " 144 if i == 7: 145 hex_line += " " 146 print "%s %s |%s|" % (reader.FormatIntPtr(addr), 147 hex_line, 148 asc_line) 149 addr += 16 150 151 if is_executable is not True and is_ascii is not True: 152 print "%s - %s" % (reader.FormatIntPtr(start), 153 reader.FormatIntPtr(start + size)) 154 for slot in xrange(start, 155 start + size, 156 reader.PointerSize()): 157 maybe_address = reader.ReadUIntPtr(slot) 158 heap_object = heap.FindObject(maybe_address) 159 print "%s: %s" % (reader.FormatIntPtr(slot), 160 reader.FormatIntPtr(maybe_address)) 161 if heap_object: 162 heap_object.Print(Printer()) 163 print 164 165 reader.ForEachMemoryRegion(dump_region) 166 167 # Heap constants generated by 'make grokdump' in v8heapconst module. 168 INSTANCE_TYPES = v8heapconst.INSTANCE_TYPES 169 KNOWN_MAPS = v8heapconst.KNOWN_MAPS 170 KNOWN_OBJECTS = v8heapconst.KNOWN_OBJECTS 171 172 # Set of structures and constants that describe the layout of minidump 173 # files. Based on MSDN and Google Breakpad. 174 175 MINIDUMP_HEADER = Descriptor([ 176 ("signature", ctypes.c_uint32), 177 ("version", ctypes.c_uint32), 178 ("stream_count", ctypes.c_uint32), 179 ("stream_directories_rva", ctypes.c_uint32), 180 ("checksum", ctypes.c_uint32), 181 ("time_date_stampt", ctypes.c_uint32), 182 ("flags", ctypes.c_uint64) 183 ]) 184 185 MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([ 186 ("data_size", ctypes.c_uint32), 187 ("rva", ctypes.c_uint32) 188 ]) 189 190 MINIDUMP_STRING = Descriptor([ 191 ("length", ctypes.c_uint32), 192 ("buffer", lambda t: ctypes.c_uint8 * (t.length + 2)) 193 ]) 194 195 MINIDUMP_DIRECTORY = Descriptor([ 196 ("stream_type", ctypes.c_uint32), 197 ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype) 198 ]) 199 200 MD_EXCEPTION_MAXIMUM_PARAMETERS = 15 201 202 MINIDUMP_EXCEPTION = Descriptor([ 203 ("code", ctypes.c_uint32), 204 ("flags", ctypes.c_uint32), 205 ("record", ctypes.c_uint64), 206 ("address", ctypes.c_uint64), 207 ("parameter_count", ctypes.c_uint32), 208 ("unused_alignment", ctypes.c_uint32), 209 ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS) 210 ]) 211 212 MINIDUMP_EXCEPTION_STREAM = Descriptor([ 213 ("thread_id", ctypes.c_uint32), 214 ("unused_alignment", ctypes.c_uint32), 215 ("exception", MINIDUMP_EXCEPTION.ctype), 216 ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype) 217 ]) 218 219 # Stream types. 220 MD_UNUSED_STREAM = 0 221 MD_RESERVED_STREAM_0 = 1 222 MD_RESERVED_STREAM_1 = 2 223 MD_THREAD_LIST_STREAM = 3 224 MD_MODULE_LIST_STREAM = 4 225 MD_MEMORY_LIST_STREAM = 5 226 MD_EXCEPTION_STREAM = 6 227 MD_SYSTEM_INFO_STREAM = 7 228 MD_THREAD_EX_LIST_STREAM = 8 229 MD_MEMORY_64_LIST_STREAM = 9 230 MD_COMMENT_STREAM_A = 10 231 MD_COMMENT_STREAM_W = 11 232 MD_HANDLE_DATA_STREAM = 12 233 MD_FUNCTION_TABLE_STREAM = 13 234 MD_UNLOADED_MODULE_LIST_STREAM = 14 235 MD_MISC_INFO_STREAM = 15 236 MD_MEMORY_INFO_LIST_STREAM = 16 237 MD_THREAD_INFO_LIST_STREAM = 17 238 MD_HANDLE_OPERATION_LIST_STREAM = 18 239 240 MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80 241 242 MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([ 243 ("control_word", ctypes.c_uint32), 244 ("status_word", ctypes.c_uint32), 245 ("tag_word", ctypes.c_uint32), 246 ("error_offset", ctypes.c_uint32), 247 ("error_selector", ctypes.c_uint32), 248 ("data_offset", ctypes.c_uint32), 249 ("data_selector", ctypes.c_uint32), 250 ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE), 251 ("cr0_npx_state", ctypes.c_uint32) 252 ]) 253 254 MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512 255 256 # Context flags. 257 MD_CONTEXT_X86 = 0x00010000 258 MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001) 259 MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002) 260 MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004) 261 MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008) 262 MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010) 263 MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020) 264 265 def EnableOnFlag(type, flag): 266 return lambda o: [None, type][int((o.context_flags & flag) != 0)] 267 268 MINIDUMP_CONTEXT_X86 = Descriptor([ 269 ("context_flags", ctypes.c_uint32), 270 # MD_CONTEXT_X86_DEBUG_REGISTERS. 271 ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 272 ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 273 ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 274 ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 275 ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 276 ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 277 # MD_CONTEXT_X86_FLOATING_POINT. 278 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype, 279 MD_CONTEXT_X86_FLOATING_POINT)), 280 # MD_CONTEXT_X86_SEGMENTS. 281 ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)), 282 ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)), 283 ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)), 284 ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)), 285 # MD_CONTEXT_X86_INTEGER. 286 ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 287 ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 288 ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 289 ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 290 ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 291 ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 292 # MD_CONTEXT_X86_CONTROL. 293 ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 294 ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 295 ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 296 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 297 ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 298 ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 299 # MD_CONTEXT_X86_EXTENDED_REGISTERS. 300 ("extended_registers", 301 EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE, 302 MD_CONTEXT_X86_EXTENDED_REGISTERS)) 303 ]) 304 305 MD_CONTEXT_ARM = 0x40000000 306 MD_CONTEXT_ARM_INTEGER = (MD_CONTEXT_ARM | 0x00000002) 307 MD_CONTEXT_ARM_FLOATING_POINT = (MD_CONTEXT_ARM | 0x00000004) 308 MD_FLOATINGSAVEAREA_ARM_FPR_COUNT = 32 309 MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT = 8 310 311 MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([ 312 ("fpscr", ctypes.c_uint64), 313 ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPR_COUNT), 314 ("extra", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT) 315 ]) 316 317 MINIDUMP_CONTEXT_ARM = Descriptor([ 318 ("context_flags", ctypes.c_uint32), 319 # MD_CONTEXT_ARM_INTEGER. 320 ("r0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 321 ("r1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 322 ("r2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 323 ("r3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 324 ("r4", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 325 ("r5", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 326 ("r6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 327 ("r7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 328 ("r8", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 329 ("r9", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 330 ("r10", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 331 ("r11", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 332 ("r12", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 333 ("sp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 334 ("lr", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 335 ("pc", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 336 ("cpsr", ctypes.c_uint32), 337 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype, 338 MD_CONTEXT_ARM_FLOATING_POINT)) 339 ]) 340 341 MD_CONTEXT_AMD64 = 0x00100000 342 MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001) 343 MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002) 344 MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004) 345 MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008) 346 MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010) 347 348 MINIDUMP_CONTEXT_AMD64 = Descriptor([ 349 ("p1_home", ctypes.c_uint64), 350 ("p2_home", ctypes.c_uint64), 351 ("p3_home", ctypes.c_uint64), 352 ("p4_home", ctypes.c_uint64), 353 ("p5_home", ctypes.c_uint64), 354 ("p6_home", ctypes.c_uint64), 355 ("context_flags", ctypes.c_uint32), 356 ("mx_csr", ctypes.c_uint32), 357 # MD_CONTEXT_AMD64_CONTROL. 358 ("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)), 359 # MD_CONTEXT_AMD64_SEGMENTS 360 ("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)), 361 ("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)), 362 ("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)), 363 ("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)), 364 # MD_CONTEXT_AMD64_CONTROL. 365 ("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)), 366 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)), 367 # MD_CONTEXT_AMD64_DEBUG_REGISTERS. 368 ("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 369 ("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 370 ("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 371 ("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 372 ("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 373 ("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 374 # MD_CONTEXT_AMD64_INTEGER. 375 ("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 376 ("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 377 ("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 378 ("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 379 # MD_CONTEXT_AMD64_CONTROL. 380 ("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)), 381 # MD_CONTEXT_AMD64_INTEGER. 382 ("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 383 ("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 384 ("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 385 ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 386 ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 387 ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 388 ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 389 ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 390 ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 391 ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 392 ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 393 # MD_CONTEXT_AMD64_CONTROL. 394 ("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)), 395 # MD_CONTEXT_AMD64_FLOATING_POINT 396 ("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26), 397 MD_CONTEXT_AMD64_FLOATING_POINT)), 398 ("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26), 399 MD_CONTEXT_AMD64_FLOATING_POINT)), 400 ("vector_control", EnableOnFlag(ctypes.c_uint64, 401 MD_CONTEXT_AMD64_FLOATING_POINT)), 402 # MD_CONTEXT_AMD64_DEBUG_REGISTERS. 403 ("debug_control", EnableOnFlag(ctypes.c_uint64, 404 MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 405 ("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64, 406 MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 407 ("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64, 408 MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 409 ("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64, 410 MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 411 ("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64, 412 MD_CONTEXT_AMD64_DEBUG_REGISTERS)) 413 ]) 414 415 MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([ 416 ("start", ctypes.c_uint64), 417 ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype) 418 ]) 419 420 MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([ 421 ("start", ctypes.c_uint64), 422 ("size", ctypes.c_uint64) 423 ]) 424 425 MINIDUMP_MEMORY_LIST = Descriptor([ 426 ("range_count", ctypes.c_uint32), 427 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count) 428 ]) 429 430 MINIDUMP_MEMORY_LIST64 = Descriptor([ 431 ("range_count", ctypes.c_uint64), 432 ("base_rva", ctypes.c_uint64), 433 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count) 434 ]) 435 436 MINIDUMP_THREAD = Descriptor([ 437 ("id", ctypes.c_uint32), 438 ("suspend_count", ctypes.c_uint32), 439 ("priority_class", ctypes.c_uint32), 440 ("priority", ctypes.c_uint32), 441 ("ted", ctypes.c_uint64), 442 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype), 443 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype) 444 ]) 445 446 MINIDUMP_THREAD_LIST = Descriptor([ 447 ("thread_count", ctypes.c_uint32), 448 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count) 449 ]) 450 451 MINIDUMP_VS_FIXEDFILEINFO = Descriptor([ 452 ("dwSignature", ctypes.c_uint32), 453 ("dwStrucVersion", ctypes.c_uint32), 454 ("dwFileVersionMS", ctypes.c_uint32), 455 ("dwFileVersionLS", ctypes.c_uint32), 456 ("dwProductVersionMS", ctypes.c_uint32), 457 ("dwProductVersionLS", ctypes.c_uint32), 458 ("dwFileFlagsMask", ctypes.c_uint32), 459 ("dwFileFlags", ctypes.c_uint32), 460 ("dwFileOS", ctypes.c_uint32), 461 ("dwFileType", ctypes.c_uint32), 462 ("dwFileSubtype", ctypes.c_uint32), 463 ("dwFileDateMS", ctypes.c_uint32), 464 ("dwFileDateLS", ctypes.c_uint32) 465 ]) 466 467 MINIDUMP_RAW_MODULE = Descriptor([ 468 ("base_of_image", ctypes.c_uint64), 469 ("size_of_image", ctypes.c_uint32), 470 ("checksum", ctypes.c_uint32), 471 ("time_date_stamp", ctypes.c_uint32), 472 ("module_name_rva", ctypes.c_uint32), 473 ("version_info", MINIDUMP_VS_FIXEDFILEINFO.ctype), 474 ("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype), 475 ("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype), 476 ("reserved0", ctypes.c_uint32 * 2), 477 ("reserved1", ctypes.c_uint32 * 2) 478 ]) 479 480 MINIDUMP_MODULE_LIST = Descriptor([ 481 ("number_of_modules", ctypes.c_uint32), 482 ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules) 483 ]) 484 485 MINIDUMP_RAW_SYSTEM_INFO = Descriptor([ 486 ("processor_architecture", ctypes.c_uint16) 487 ]) 488 489 MD_CPU_ARCHITECTURE_X86 = 0 490 MD_CPU_ARCHITECTURE_ARM = 5 491 MD_CPU_ARCHITECTURE_AMD64 = 9 492 493 class FuncSymbol: 494 def __init__(self, start, size, name): 495 self.start = start 496 self.end = self.start + size 497 self.name = name 498 499 def __cmp__(self, other): 500 if isinstance(other, FuncSymbol): 501 return self.start - other.start 502 return self.start - other 503 504 def Covers(self, addr): 505 return (self.start <= addr) and (addr < self.end) 506 507 class MinidumpReader(object): 508 """Minidump (.dmp) reader.""" 509 510 _HEADER_MAGIC = 0x504d444d 511 512 def __init__(self, options, minidump_name): 513 self.minidump_name = minidump_name 514 self.minidump_file = open(minidump_name, "r") 515 self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE) 516 self.header = MINIDUMP_HEADER.Read(self.minidump, 0) 517 if self.header.signature != MinidumpReader._HEADER_MAGIC: 518 print >>sys.stderr, "Warning: Unsupported minidump header magic!" 519 DebugPrint(self.header) 520 directories = [] 521 offset = self.header.stream_directories_rva 522 for _ in xrange(self.header.stream_count): 523 directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset)) 524 offset += MINIDUMP_DIRECTORY.size 525 self.arch = None 526 self.exception = None 527 self.exception_context = None 528 self.memory_list = None 529 self.memory_list64 = None 530 self.module_list = None 531 self.thread_map = {} 532 533 self.symdir = options.symdir 534 self.modules_with_symbols = [] 535 self.symbols = [] 536 537 # Find MDRawSystemInfo stream and determine arch. 538 for d in directories: 539 if d.stream_type == MD_SYSTEM_INFO_STREAM: 540 system_info = MINIDUMP_RAW_SYSTEM_INFO.Read( 541 self.minidump, d.location.rva) 542 self.arch = system_info.processor_architecture 543 assert self.arch in [MD_CPU_ARCHITECTURE_AMD64, 544 MD_CPU_ARCHITECTURE_ARM, 545 MD_CPU_ARCHITECTURE_X86] 546 assert not self.arch is None 547 548 for d in directories: 549 DebugPrint(d) 550 if d.stream_type == MD_EXCEPTION_STREAM: 551 self.exception = MINIDUMP_EXCEPTION_STREAM.Read( 552 self.minidump, d.location.rva) 553 DebugPrint(self.exception) 554 if self.arch == MD_CPU_ARCHITECTURE_X86: 555 self.exception_context = MINIDUMP_CONTEXT_X86.Read( 556 self.minidump, self.exception.thread_context.rva) 557 elif self.arch == MD_CPU_ARCHITECTURE_AMD64: 558 self.exception_context = MINIDUMP_CONTEXT_AMD64.Read( 559 self.minidump, self.exception.thread_context.rva) 560 elif self.arch == MD_CPU_ARCHITECTURE_ARM: 561 self.exception_context = MINIDUMP_CONTEXT_ARM.Read( 562 self.minidump, self.exception.thread_context.rva) 563 DebugPrint(self.exception_context) 564 elif d.stream_type == MD_THREAD_LIST_STREAM: 565 thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva) 566 assert ctypes.sizeof(thread_list) == d.location.data_size 567 DebugPrint(thread_list) 568 for thread in thread_list.threads: 569 DebugPrint(thread) 570 self.thread_map[thread.id] = thread 571 elif d.stream_type == MD_MODULE_LIST_STREAM: 572 assert self.module_list is None 573 self.module_list = MINIDUMP_MODULE_LIST.Read( 574 self.minidump, d.location.rva) 575 assert ctypes.sizeof(self.module_list) == d.location.data_size 576 elif d.stream_type == MD_MEMORY_LIST_STREAM: 577 print >>sys.stderr, "Warning: This is not a full minidump!" 578 assert self.memory_list is None 579 self.memory_list = MINIDUMP_MEMORY_LIST.Read( 580 self.minidump, d.location.rva) 581 assert ctypes.sizeof(self.memory_list) == d.location.data_size 582 DebugPrint(self.memory_list) 583 elif d.stream_type == MD_MEMORY_64_LIST_STREAM: 584 assert self.memory_list64 is None 585 self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read( 586 self.minidump, d.location.rva) 587 assert ctypes.sizeof(self.memory_list64) == d.location.data_size 588 DebugPrint(self.memory_list64) 589 590 def IsValidAddress(self, address): 591 return self.FindLocation(address) is not None 592 593 def ReadU8(self, address): 594 location = self.FindLocation(address) 595 return ctypes.c_uint8.from_buffer(self.minidump, location).value 596 597 def ReadU32(self, address): 598 location = self.FindLocation(address) 599 return ctypes.c_uint32.from_buffer(self.minidump, location).value 600 601 def ReadU64(self, address): 602 location = self.FindLocation(address) 603 return ctypes.c_uint64.from_buffer(self.minidump, location).value 604 605 def ReadUIntPtr(self, address): 606 if self.arch == MD_CPU_ARCHITECTURE_AMD64: 607 return self.ReadU64(address) 608 elif self.arch == MD_CPU_ARCHITECTURE_ARM: 609 return self.ReadU32(address) 610 elif self.arch == MD_CPU_ARCHITECTURE_X86: 611 return self.ReadU32(address) 612 613 def ReadBytes(self, address, size): 614 location = self.FindLocation(address) 615 return self.minidump[location:location + size] 616 617 def _ReadWord(self, location): 618 if self.arch == MD_CPU_ARCHITECTURE_AMD64: 619 return ctypes.c_uint64.from_buffer(self.minidump, location).value 620 elif self.arch == MD_CPU_ARCHITECTURE_ARM: 621 return ctypes.c_uint32.from_buffer(self.minidump, location).value 622 elif self.arch == MD_CPU_ARCHITECTURE_X86: 623 return ctypes.c_uint32.from_buffer(self.minidump, location).value 624 625 def IsProbableASCIIRegion(self, location, length): 626 ascii_bytes = 0 627 non_ascii_bytes = 0 628 for loc in xrange(location, location + length): 629 byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value 630 if byte >= 0x7f: 631 non_ascii_bytes += 1 632 if byte < 0x20 and byte != 0: 633 non_ascii_bytes += 1 634 if byte < 0x7f and byte >= 0x20: 635 ascii_bytes += 1 636 if byte == 0xa: # newline 637 ascii_bytes += 1 638 if ascii_bytes * 10 <= length: 639 return False 640 if length > 0 and ascii_bytes > non_ascii_bytes * 7: 641 return True 642 if ascii_bytes > non_ascii_bytes * 3: 643 return None # Maybe 644 return False 645 646 def IsProbableExecutableRegion(self, location, length): 647 opcode_bytes = 0 648 sixty_four = self.arch == MD_CPU_ARCHITECTURE_AMD64 649 for loc in xrange(location, location + length): 650 byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value 651 if (byte == 0x8b or # mov 652 byte == 0x89 or # mov reg-reg 653 (byte & 0xf0) == 0x50 or # push/pop 654 (sixty_four and (byte & 0xf0) == 0x40) or # rex prefix 655 byte == 0xc3 or # return 656 byte == 0x74 or # jeq 657 byte == 0x84 or # jeq far 658 byte == 0x75 or # jne 659 byte == 0x85 or # jne far 660 byte == 0xe8 or # call 661 byte == 0xe9 or # jmp far 662 byte == 0xeb): # jmp near 663 opcode_bytes += 1 664 opcode_percent = (opcode_bytes * 100) / length 665 threshold = 20 666 if opcode_percent > threshold + 2: 667 return True 668 if opcode_percent > threshold - 2: 669 return None # Maybe 670 return False 671 672 def FindRegion(self, addr): 673 answer = [-1, -1] 674 def is_in(reader, start, size, location): 675 if addr >= start and addr < start + size: 676 answer[0] = start 677 answer[1] = size 678 self.ForEachMemoryRegion(is_in) 679 if answer[0] == -1: 680 return None 681 return answer 682 683 def ForEachMemoryRegion(self, cb): 684 if self.memory_list64 is not None: 685 for r in self.memory_list64.ranges: 686 location = self.memory_list64.base_rva + offset 687 cb(self, r.start, r.size, location) 688 offset += r.size 689 690 if self.memory_list is not None: 691 for r in self.memory_list.ranges: 692 cb(self, r.start, r.memory.data_size, r.memory.rva) 693 694 def FindWord(self, word, alignment=0): 695 def search_inside_region(reader, start, size, location): 696 location = (location + alignment) & ~alignment 697 for loc in xrange(location, location + size - self.PointerSize()): 698 if reader._ReadWord(loc) == word: 699 slot = start + (loc - location) 700 print "%s: %s" % (reader.FormatIntPtr(slot), 701 reader.FormatIntPtr(word)) 702 self.ForEachMemoryRegion(search_inside_region) 703 704 def FindLocation(self, address): 705 offset = 0 706 if self.memory_list64 is not None: 707 for r in self.memory_list64.ranges: 708 if r.start <= address < r.start + r.size: 709 return self.memory_list64.base_rva + offset + address - r.start 710 offset += r.size 711 if self.memory_list is not None: 712 for r in self.memory_list.ranges: 713 if r.start <= address < r.start + r.memory.data_size: 714 return r.memory.rva + address - r.start 715 return None 716 717 def GetDisasmLines(self, address, size): 718 def CountUndefinedInstructions(lines): 719 pattern = "<UNDEFINED>" 720 return sum([line.count(pattern) for (ignore, line) in lines]) 721 722 location = self.FindLocation(address) 723 if location is None: return [] 724 arch = None 725 possible_objdump_flags = [""] 726 if self.arch == MD_CPU_ARCHITECTURE_X86: 727 arch = "ia32" 728 elif self.arch == MD_CPU_ARCHITECTURE_ARM: 729 arch = "arm" 730 possible_objdump_flags = ["", "--disassembler-options=force-thumb"] 731 elif self.arch == MD_CPU_ARCHITECTURE_AMD64: 732 arch = "x64" 733 results = [ disasm.GetDisasmLines(self.minidump_name, 734 location, 735 size, 736 arch, 737 False, 738 objdump_flags) 739 for objdump_flags in possible_objdump_flags ] 740 return min(results, key=CountUndefinedInstructions) 741 742 743 def Dispose(self): 744 self.minidump.close() 745 self.minidump_file.close() 746 747 def ExceptionIP(self): 748 if self.arch == MD_CPU_ARCHITECTURE_AMD64: 749 return self.exception_context.rip 750 elif self.arch == MD_CPU_ARCHITECTURE_ARM: 751 return self.exception_context.pc 752 elif self.arch == MD_CPU_ARCHITECTURE_X86: 753 return self.exception_context.eip 754 755 def ExceptionSP(self): 756 if self.arch == MD_CPU_ARCHITECTURE_AMD64: 757 return self.exception_context.rsp 758 elif self.arch == MD_CPU_ARCHITECTURE_ARM: 759 return self.exception_context.sp 760 elif self.arch == MD_CPU_ARCHITECTURE_X86: 761 return self.exception_context.esp 762 763 def ExceptionFP(self): 764 if self.arch == MD_CPU_ARCHITECTURE_AMD64: 765 return self.exception_context.rbp 766 elif self.arch == MD_CPU_ARCHITECTURE_ARM: 767 return None 768 elif self.arch == MD_CPU_ARCHITECTURE_X86: 769 return self.exception_context.ebp 770 771 def FormatIntPtr(self, value): 772 if self.arch == MD_CPU_ARCHITECTURE_AMD64: 773 return "%016x" % value 774 elif self.arch == MD_CPU_ARCHITECTURE_ARM: 775 return "%08x" % value 776 elif self.arch == MD_CPU_ARCHITECTURE_X86: 777 return "%08x" % value 778 779 def PointerSize(self): 780 if self.arch == MD_CPU_ARCHITECTURE_AMD64: 781 return 8 782 elif self.arch == MD_CPU_ARCHITECTURE_ARM: 783 return 4 784 elif self.arch == MD_CPU_ARCHITECTURE_X86: 785 return 4 786 787 def Register(self, name): 788 return self.exception_context.__getattribute__(name) 789 790 def ReadMinidumpString(self, rva): 791 string = bytearray(MINIDUMP_STRING.Read(self.minidump, rva).buffer) 792 string = string.decode("utf16") 793 return string[0:len(string) - 1] 794 795 # Load FUNC records from a BreakPad symbol file 796 # 797 # http://code.google.com/p/google-breakpad/wiki/SymbolFiles 798 # 799 def _LoadSymbolsFrom(self, symfile, baseaddr): 800 print "Loading symbols from %s" % (symfile) 801 funcs = [] 802 with open(symfile) as f: 803 for line in f: 804 result = re.match( 805 r"^FUNC ([a-f0-9]+) ([a-f0-9]+) ([a-f0-9]+) (.*)$", line) 806 if result is not None: 807 start = int(result.group(1), 16) 808 size = int(result.group(2), 16) 809 name = result.group(4).rstrip() 810 bisect.insort_left(self.symbols, 811 FuncSymbol(baseaddr + start, size, name)) 812 print " ... done" 813 814 def TryLoadSymbolsFor(self, modulename, module): 815 try: 816 symfile = os.path.join(self.symdir, 817 modulename.replace('.', '_') + ".pdb.sym") 818 if os.path.isfile(symfile): 819 self._LoadSymbolsFrom(symfile, module.base_of_image) 820 self.modules_with_symbols.append(module) 821 except Exception as e: 822 print " ... failure (%s)" % (e) 823 824 # Returns true if address is covered by some module that has loaded symbols. 825 def _IsInModuleWithSymbols(self, addr): 826 for module in self.modules_with_symbols: 827 start = module.base_of_image 828 end = start + module.size_of_image 829 if (start <= addr) and (addr < end): 830 return True 831 return False 832 833 # Find symbol covering the given address and return its name in format 834 # <symbol name>+<offset from the start> 835 def FindSymbol(self, addr): 836 if not self._IsInModuleWithSymbols(addr): 837 return None 838 839 i = bisect.bisect_left(self.symbols, addr) 840 symbol = None 841 if (0 < i) and self.symbols[i - 1].Covers(addr): 842 symbol = self.symbols[i - 1] 843 elif (i < len(self.symbols)) and self.symbols[i].Covers(addr): 844 symbol = self.symbols[i] 845 else: 846 return None 847 diff = addr - symbol.start 848 return "%s+0x%x" % (symbol.name, diff) 849 850 851 class Printer(object): 852 """Printer with indentation support.""" 853 854 def __init__(self): 855 self.indent = 0 856 857 def Indent(self): 858 self.indent += 2 859 860 def Dedent(self): 861 self.indent -= 2 862 863 def Print(self, string): 864 print "%s%s" % (self._IndentString(), string) 865 866 def PrintLines(self, lines): 867 indent = self._IndentString() 868 print "\n".join("%s%s" % (indent, line) for line in lines) 869 870 def _IndentString(self): 871 return self.indent * " " 872 873 874 ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+") 875 876 877 def FormatDisasmLine(start, heap, line): 878 line_address = start + line[0] 879 stack_slot = heap.stack_map.get(line_address) 880 marker = " " 881 if stack_slot: 882 marker = "=>" 883 code = AnnotateAddresses(heap, line[1]) 884 return "%s%08x %08x: %s" % (marker, line_address, line[0], code) 885 886 887 def AnnotateAddresses(heap, line): 888 extra = [] 889 for m in ADDRESS_RE.finditer(line): 890 maybe_address = int(m.group(0), 16) 891 object = heap.FindObject(maybe_address) 892 if not object: continue 893 extra.append(str(object)) 894 if len(extra) == 0: return line 895 return "%s ;; %s" % (line, ", ".join(extra)) 896 897 898 class HeapObject(object): 899 def __init__(self, heap, map, address): 900 self.heap = heap 901 self.map = map 902 self.address = address 903 904 def Is(self, cls): 905 return isinstance(self, cls) 906 907 def Print(self, p): 908 p.Print(str(self)) 909 910 def __str__(self): 911 return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address), 912 INSTANCE_TYPES[self.map.instance_type]) 913 914 def ObjectField(self, offset): 915 field_value = self.heap.reader.ReadUIntPtr(self.address + offset) 916 return self.heap.FindObjectOrSmi(field_value) 917 918 def SmiField(self, offset): 919 field_value = self.heap.reader.ReadUIntPtr(self.address + offset) 920 assert (field_value & 1) == 0 921 return field_value / 2 922 923 924 class Map(HeapObject): 925 def Decode(self, offset, size, value): 926 return (value >> offset) & ((1 << size) - 1) 927 928 # Instance Sizes 929 def InstanceSizesOffset(self): 930 return self.heap.PointerSize() 931 932 def InstanceSizeOffset(self): 933 return self.InstanceSizesOffset() 934 935 def InObjectProperties(self): 936 return self.InstanceSizeOffset() + 1 937 938 def PreAllocatedPropertyFields(self): 939 return self.InObjectProperties() + 1 940 941 def VisitorId(self): 942 return self.PreAllocatedPropertyFields() + 1 943 944 # Instance Attributes 945 def InstanceAttributesOffset(self): 946 return self.InstanceSizesOffset() + self.heap.IntSize() 947 948 def InstanceTypeOffset(self): 949 return self.InstanceAttributesOffset() 950 951 def UnusedPropertyFieldsOffset(self): 952 return self.InstanceTypeOffset() + 1 953 954 def BitFieldOffset(self): 955 return self.UnusedPropertyFieldsOffset() + 1 956 957 def BitField2Offset(self): 958 return self.BitFieldOffset() + 1 959 960 # Other fields 961 def PrototypeOffset(self): 962 return self.InstanceAttributesOffset() + self.heap.IntSize() 963 964 def ConstructorOffset(self): 965 return self.PrototypeOffset() + self.heap.PointerSize() 966 967 def TransitionsOrBackPointerOffset(self): 968 return self.ConstructorOffset() + self.heap.PointerSize() 969 970 def DescriptorsOffset(self): 971 return self.TransitionsOrBackPointerOffset() + self.heap.PointerSize() 972 973 def CodeCacheOffset(self): 974 return self.DescriptorsOffset() + self.heap.PointerSize() 975 976 def DependentCodeOffset(self): 977 return self.CodeCacheOffset() + self.heap.PointerSize() 978 979 def BitField3Offset(self): 980 return self.DependentCodeOffset() + self.heap.PointerSize() 981 982 def ReadByte(self, offset): 983 return self.heap.reader.ReadU8(self.address + offset) 984 985 def Print(self, p): 986 p.Print("Map(%08x)" % (self.address)) 987 p.Print("- size: %d, inobject: %d, preallocated: %d, visitor: %d" % ( 988 self.ReadByte(self.InstanceSizeOffset()), 989 self.ReadByte(self.InObjectProperties()), 990 self.ReadByte(self.PreAllocatedPropertyFields()), 991 self.VisitorId())) 992 993 bitfield = self.ReadByte(self.BitFieldOffset()) 994 bitfield2 = self.ReadByte(self.BitField2Offset()) 995 p.Print("- %s, unused: %d, bf: %d, bf2: %d" % ( 996 INSTANCE_TYPES[self.ReadByte(self.InstanceTypeOffset())], 997 self.ReadByte(self.UnusedPropertyFieldsOffset()), 998 bitfield, bitfield2)) 999 1000 p.Print("- kind: %s" % (self.Decode(3, 5, bitfield2))) 1001 1002 bitfield3 = self.ObjectField(self.BitField3Offset()) 1003 p.Print( 1004 "- EnumLength: %d NumberOfOwnDescriptors: %d OwnsDescriptors: %s" % ( 1005 self.Decode(0, 11, bitfield3), 1006 self.Decode(11, 11, bitfield3), 1007 self.Decode(25, 1, bitfield3))) 1008 p.Print("- IsShared: %s" % (self.Decode(22, 1, bitfield3))) 1009 p.Print("- FunctionWithPrototype: %s" % (self.Decode(23, 1, bitfield3))) 1010 p.Print("- DictionaryMap: %s" % (self.Decode(24, 1, bitfield3))) 1011 1012 descriptors = self.ObjectField(self.DescriptorsOffset()) 1013 if descriptors.__class__ == FixedArray: 1014 DescriptorArray(descriptors).Print(p) 1015 else: 1016 p.Print("Descriptors: %s" % (descriptors)) 1017 1018 transitions = self.ObjectField(self.TransitionsOrBackPointerOffset()) 1019 if transitions.__class__ == FixedArray: 1020 TransitionArray(transitions).Print(p) 1021 else: 1022 p.Print("TransitionsOrBackPointer: %s" % (transitions)) 1023 1024 def __init__(self, heap, map, address): 1025 HeapObject.__init__(self, heap, map, address) 1026 self.instance_type = \ 1027 heap.reader.ReadU8(self.address + self.InstanceTypeOffset()) 1028 1029 1030 class String(HeapObject): 1031 def LengthOffset(self): 1032 return self.heap.PointerSize() 1033 1034 def __init__(self, heap, map, address): 1035 HeapObject.__init__(self, heap, map, address) 1036 self.length = self.SmiField(self.LengthOffset()) 1037 1038 def GetChars(self): 1039 return "?string?" 1040 1041 def Print(self, p): 1042 p.Print(str(self)) 1043 1044 def __str__(self): 1045 return "\"%s\"" % self.GetChars() 1046 1047 1048 class SeqString(String): 1049 def CharsOffset(self): 1050 return self.heap.PointerSize() * 3 1051 1052 def __init__(self, heap, map, address): 1053 String.__init__(self, heap, map, address) 1054 self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(), 1055 self.length) 1056 1057 def GetChars(self): 1058 return self.chars 1059 1060 1061 class ExternalString(String): 1062 # TODO(vegorov) fix ExternalString for X64 architecture 1063 RESOURCE_OFFSET = 12 1064 1065 WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4 1066 WEBKIT_STRING_IMPL_CHARS_OFFSET = 8 1067 1068 def __init__(self, heap, map, address): 1069 String.__init__(self, heap, map, address) 1070 reader = heap.reader 1071 self.resource = \ 1072 reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET) 1073 self.chars = "?external string?" 1074 if not reader.IsValidAddress(self.resource): return 1075 string_impl_address = self.resource + \ 1076 ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET 1077 if not reader.IsValidAddress(string_impl_address): return 1078 string_impl = reader.ReadU32(string_impl_address) 1079 chars_ptr_address = string_impl + \ 1080 ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET 1081 if not reader.IsValidAddress(chars_ptr_address): return 1082 chars_ptr = reader.ReadU32(chars_ptr_address) 1083 if not reader.IsValidAddress(chars_ptr): return 1084 raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length) 1085 self.chars = codecs.getdecoder("utf16")(raw_chars)[0] 1086 1087 def GetChars(self): 1088 return self.chars 1089 1090 1091 class ConsString(String): 1092 def LeftOffset(self): 1093 return self.heap.PointerSize() * 3 1094 1095 def RightOffset(self): 1096 return self.heap.PointerSize() * 4 1097 1098 def __init__(self, heap, map, address): 1099 String.__init__(self, heap, map, address) 1100 self.left = self.ObjectField(self.LeftOffset()) 1101 self.right = self.ObjectField(self.RightOffset()) 1102 1103 def GetChars(self): 1104 try: 1105 return self.left.GetChars() + self.right.GetChars() 1106 except: 1107 return "***CAUGHT EXCEPTION IN GROKDUMP***" 1108 1109 1110 class Oddball(HeapObject): 1111 # Should match declarations in objects.h 1112 KINDS = [ 1113 "False", 1114 "True", 1115 "TheHole", 1116 "Null", 1117 "ArgumentMarker", 1118 "Undefined", 1119 "Other" 1120 ] 1121 1122 def ToStringOffset(self): 1123 return self.heap.PointerSize() 1124 1125 def ToNumberOffset(self): 1126 return self.ToStringOffset() + self.heap.PointerSize() 1127 1128 def KindOffset(self): 1129 return self.ToNumberOffset() + self.heap.PointerSize() 1130 1131 def __init__(self, heap, map, address): 1132 HeapObject.__init__(self, heap, map, address) 1133 self.to_string = self.ObjectField(self.ToStringOffset()) 1134 self.kind = self.SmiField(self.KindOffset()) 1135 1136 def Print(self, p): 1137 p.Print(str(self)) 1138 1139 def __str__(self): 1140 if self.to_string: 1141 return "Oddball(%08x, <%s>)" % (self.address, self.to_string.GetChars()) 1142 else: 1143 kind = "???" 1144 if 0 <= self.kind < len(Oddball.KINDS): 1145 kind = Oddball.KINDS[self.kind] 1146 return "Oddball(%08x, kind=%s)" % (self.address, kind) 1147 1148 1149 class FixedArray(HeapObject): 1150 def LengthOffset(self): 1151 return self.heap.PointerSize() 1152 1153 def ElementsOffset(self): 1154 return self.heap.PointerSize() * 2 1155 1156 def MemberOffset(self, i): 1157 return self.ElementsOffset() + self.heap.PointerSize() * i 1158 1159 def Get(self, i): 1160 return self.ObjectField(self.MemberOffset(i)) 1161 1162 def __init__(self, heap, map, address): 1163 HeapObject.__init__(self, heap, map, address) 1164 self.length = self.SmiField(self.LengthOffset()) 1165 1166 def Print(self, p): 1167 p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address)) 1168 p.Indent() 1169 p.Print("length: %d" % self.length) 1170 base_offset = self.ElementsOffset() 1171 for i in xrange(self.length): 1172 offset = base_offset + 4 * i 1173 try: 1174 p.Print("[%08d] = %s" % (i, self.ObjectField(offset))) 1175 except TypeError: 1176 p.Dedent() 1177 p.Print("...") 1178 p.Print("}") 1179 return 1180 p.Dedent() 1181 p.Print("}") 1182 1183 def __str__(self): 1184 return "FixedArray(%08x, length=%d)" % (self.address, self.length) 1185 1186 1187 class DescriptorArray(object): 1188 def __init__(self, array): 1189 self.array = array 1190 1191 def Length(self): 1192 return self.array.Get(0) 1193 1194 def Decode(self, offset, size, value): 1195 return (value >> offset) & ((1 << size) - 1) 1196 1197 TYPES = [ 1198 "normal", 1199 "field", 1200 "function", 1201 "callbacks" 1202 ] 1203 1204 def Type(self, value): 1205 return DescriptorArray.TYPES[self.Decode(0, 3, value)] 1206 1207 def Attributes(self, value): 1208 attributes = self.Decode(3, 3, value) 1209 result = [] 1210 if (attributes & 0): result += ["ReadOnly"] 1211 if (attributes & 1): result += ["DontEnum"] 1212 if (attributes & 2): result += ["DontDelete"] 1213 return "[" + (",".join(result)) + "]" 1214 1215 def Deleted(self, value): 1216 return self.Decode(6, 1, value) == 1 1217 1218 def Storage(self, value): 1219 return self.Decode(7, 11, value) 1220 1221 def Pointer(self, value): 1222 return self.Decode(18, 11, value) 1223 1224 def Details(self, di, value): 1225 return ( 1226 di, 1227 self.Type(value), 1228 self.Attributes(value), 1229 self.Storage(value), 1230 self.Pointer(value) 1231 ) 1232 1233 1234 def Print(self, p): 1235 length = self.Length() 1236 array = self.array 1237 1238 p.Print("Descriptors(%08x, length=%d)" % (array.address, length)) 1239 p.Print("[et] %s" % (array.Get(1))) 1240 1241 for di in xrange(length): 1242 i = 2 + di * 3 1243 p.Print("0x%x" % (array.address + array.MemberOffset(i))) 1244 p.Print("[%i] name: %s" % (di, array.Get(i + 0))) 1245 p.Print("[%i] details: %s %s enum %i pointer %i" % \ 1246 self.Details(di, array.Get(i + 1))) 1247 p.Print("[%i] value: %s" % (di, array.Get(i + 2))) 1248 1249 end = self.array.length // 3 1250 if length != end: 1251 p.Print("[%i-%i] slack descriptors" % (length, end)) 1252 1253 1254 class TransitionArray(object): 1255 def __init__(self, array): 1256 self.array = array 1257 1258 def IsSimpleTransition(self): 1259 return self.array.length <= 2 1260 1261 def Length(self): 1262 # SimpleTransition cases 1263 if self.IsSimpleTransition(): 1264 return self.array.length - 1 1265 return (self.array.length - 3) // 2 1266 1267 def Print(self, p): 1268 length = self.Length() 1269 array = self.array 1270 1271 p.Print("Transitions(%08x, length=%d)" % (array.address, length)) 1272 p.Print("[backpointer] %s" % (array.Get(0))) 1273 if self.IsSimpleTransition(): 1274 if length == 1: 1275 p.Print("[simple target] %s" % (array.Get(1))) 1276 return 1277 1278 elements = array.Get(1) 1279 if elements is not None: 1280 p.Print("[elements ] %s" % (elements)) 1281 1282 prototype = array.Get(2) 1283 if prototype is not None: 1284 p.Print("[prototype ] %s" % (prototype)) 1285 1286 for di in xrange(length): 1287 i = 3 + di * 2 1288 p.Print("[%i] symbol: %s" % (di, array.Get(i + 0))) 1289 p.Print("[%i] target: %s" % (di, array.Get(i + 1))) 1290 1291 1292 class JSFunction(HeapObject): 1293 def CodeEntryOffset(self): 1294 return 3 * self.heap.PointerSize() 1295 1296 def SharedOffset(self): 1297 return 5 * self.heap.PointerSize() 1298 1299 def __init__(self, heap, map, address): 1300 HeapObject.__init__(self, heap, map, address) 1301 code_entry = \ 1302 heap.reader.ReadU32(self.address + self.CodeEntryOffset()) 1303 self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1) 1304 self.shared = self.ObjectField(self.SharedOffset()) 1305 1306 def Print(self, p): 1307 source = "\n".join(" %s" % line for line in self._GetSource().split("\n")) 1308 p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address)) 1309 p.Indent() 1310 p.Print("inferred name: %s" % self.shared.inferred_name) 1311 if self.shared.script.Is(Script) and self.shared.script.name.Is(String): 1312 p.Print("script name: %s" % self.shared.script.name) 1313 p.Print("source:") 1314 p.PrintLines(self._GetSource().split("\n")) 1315 p.Print("code:") 1316 self.code.Print(p) 1317 if self.code != self.shared.code: 1318 p.Print("unoptimized code:") 1319 self.shared.code.Print(p) 1320 p.Dedent() 1321 p.Print("}") 1322 1323 def __str__(self): 1324 inferred_name = "" 1325 if self.shared.Is(SharedFunctionInfo): 1326 inferred_name = self.shared.inferred_name 1327 return "JSFunction(%s, %s)" % \ 1328 (self.heap.reader.FormatIntPtr(self.address), inferred_name) 1329 1330 def _GetSource(self): 1331 source = "?source?" 1332 start = self.shared.start_position 1333 end = self.shared.end_position 1334 if not self.shared.script.Is(Script): return source 1335 script_source = self.shared.script.source 1336 if not script_source.Is(String): return source 1337 return script_source.GetChars()[start:end] 1338 1339 1340 class SharedFunctionInfo(HeapObject): 1341 def CodeOffset(self): 1342 return 2 * self.heap.PointerSize() 1343 1344 def ScriptOffset(self): 1345 return 7 * self.heap.PointerSize() 1346 1347 def InferredNameOffset(self): 1348 return 9 * self.heap.PointerSize() 1349 1350 def EndPositionOffset(self): 1351 return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize() 1352 1353 def StartPositionAndTypeOffset(self): 1354 return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize() 1355 1356 def __init__(self, heap, map, address): 1357 HeapObject.__init__(self, heap, map, address) 1358 self.code = self.ObjectField(self.CodeOffset()) 1359 self.script = self.ObjectField(self.ScriptOffset()) 1360 self.inferred_name = self.ObjectField(self.InferredNameOffset()) 1361 if heap.PointerSize() == 8: 1362 start_position_and_type = \ 1363 heap.reader.ReadU32(self.StartPositionAndTypeOffset()) 1364 self.start_position = start_position_and_type >> 2 1365 pseudo_smi_end_position = \ 1366 heap.reader.ReadU32(self.EndPositionOffset()) 1367 self.end_position = pseudo_smi_end_position >> 2 1368 else: 1369 start_position_and_type = \ 1370 self.SmiField(self.StartPositionAndTypeOffset()) 1371 self.start_position = start_position_and_type >> 2 1372 self.end_position = \ 1373 self.SmiField(self.EndPositionOffset()) 1374 1375 1376 class Script(HeapObject): 1377 def SourceOffset(self): 1378 return self.heap.PointerSize() 1379 1380 def NameOffset(self): 1381 return self.SourceOffset() + self.heap.PointerSize() 1382 1383 def __init__(self, heap, map, address): 1384 HeapObject.__init__(self, heap, map, address) 1385 self.source = self.ObjectField(self.SourceOffset()) 1386 self.name = self.ObjectField(self.NameOffset()) 1387 1388 1389 class CodeCache(HeapObject): 1390 def DefaultCacheOffset(self): 1391 return self.heap.PointerSize() 1392 1393 def NormalTypeCacheOffset(self): 1394 return self.DefaultCacheOffset() + self.heap.PointerSize() 1395 1396 def __init__(self, heap, map, address): 1397 HeapObject.__init__(self, heap, map, address) 1398 self.default_cache = self.ObjectField(self.DefaultCacheOffset()) 1399 self.normal_type_cache = self.ObjectField(self.NormalTypeCacheOffset()) 1400 1401 def Print(self, p): 1402 p.Print("CodeCache(%s) {" % self.heap.reader.FormatIntPtr(self.address)) 1403 p.Indent() 1404 p.Print("default cache: %s" % self.default_cache) 1405 p.Print("normal type cache: %s" % self.normal_type_cache) 1406 p.Dedent() 1407 p.Print("}") 1408 1409 1410 class Code(HeapObject): 1411 CODE_ALIGNMENT_MASK = (1 << 5) - 1 1412 1413 def InstructionSizeOffset(self): 1414 return self.heap.PointerSize() 1415 1416 @staticmethod 1417 def HeaderSize(heap): 1418 return (heap.PointerSize() + heap.IntSize() + \ 1419 4 * heap.PointerSize() + 3 * heap.IntSize() + \ 1420 Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK 1421 1422 def __init__(self, heap, map, address): 1423 HeapObject.__init__(self, heap, map, address) 1424 self.entry = self.address + Code.HeaderSize(heap) 1425 self.instruction_size = \ 1426 heap.reader.ReadU32(self.address + self.InstructionSizeOffset()) 1427 1428 def Print(self, p): 1429 lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size) 1430 p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address)) 1431 p.Indent() 1432 p.Print("instruction_size: %d" % self.instruction_size) 1433 p.PrintLines(self._FormatLine(line) for line in lines) 1434 p.Dedent() 1435 p.Print("}") 1436 1437 def _FormatLine(self, line): 1438 return FormatDisasmLine(self.entry, self.heap, line) 1439 1440 1441 class V8Heap(object): 1442 CLASS_MAP = { 1443 "SYMBOL_TYPE": SeqString, 1444 "ASCII_SYMBOL_TYPE": SeqString, 1445 "CONS_SYMBOL_TYPE": ConsString, 1446 "CONS_ASCII_SYMBOL_TYPE": ConsString, 1447 "EXTERNAL_SYMBOL_TYPE": ExternalString, 1448 "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString, 1449 "EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString, 1450 "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString, 1451 "SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString, 1452 "SHORT_EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString, 1453 "STRING_TYPE": SeqString, 1454 "ASCII_STRING_TYPE": SeqString, 1455 "CONS_STRING_TYPE": ConsString, 1456 "CONS_ASCII_STRING_TYPE": ConsString, 1457 "EXTERNAL_STRING_TYPE": ExternalString, 1458 "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE": ExternalString, 1459 "EXTERNAL_ASCII_STRING_TYPE": ExternalString, 1460 "MAP_TYPE": Map, 1461 "ODDBALL_TYPE": Oddball, 1462 "FIXED_ARRAY_TYPE": FixedArray, 1463 "JS_FUNCTION_TYPE": JSFunction, 1464 "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo, 1465 "SCRIPT_TYPE": Script, 1466 "CODE_CACHE_TYPE": CodeCache, 1467 "CODE_TYPE": Code, 1468 } 1469 1470 def __init__(self, reader, stack_map): 1471 self.reader = reader 1472 self.stack_map = stack_map 1473 self.objects = {} 1474 1475 def FindObjectOrSmi(self, tagged_address): 1476 if (tagged_address & 1) == 0: return tagged_address / 2 1477 return self.FindObject(tagged_address) 1478 1479 def FindObject(self, tagged_address): 1480 if tagged_address in self.objects: 1481 return self.objects[tagged_address] 1482 if (tagged_address & self.ObjectAlignmentMask()) != 1: return None 1483 address = tagged_address - 1 1484 if not self.reader.IsValidAddress(address): return None 1485 map_tagged_address = self.reader.ReadUIntPtr(address) 1486 if tagged_address == map_tagged_address: 1487 # Meta map? 1488 meta_map = Map(self, None, address) 1489 instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type) 1490 if instance_type_name != "MAP_TYPE": return None 1491 meta_map.map = meta_map 1492 object = meta_map 1493 else: 1494 map = self.FindMap(map_tagged_address) 1495 if map is None: return None 1496 instance_type_name = INSTANCE_TYPES.get(map.instance_type) 1497 if instance_type_name is None: return None 1498 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject) 1499 object = cls(self, map, address) 1500 self.objects[tagged_address] = object 1501 return object 1502 1503 def FindMap(self, tagged_address): 1504 if (tagged_address & self.MapAlignmentMask()) != 1: return None 1505 address = tagged_address - 1 1506 if not self.reader.IsValidAddress(address): return None 1507 object = Map(self, None, address) 1508 return object 1509 1510 def IntSize(self): 1511 return 4 1512 1513 def PointerSize(self): 1514 return self.reader.PointerSize() 1515 1516 def ObjectAlignmentMask(self): 1517 return self.PointerSize() - 1 1518 1519 def MapAlignmentMask(self): 1520 if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64: 1521 return (1 << 4) - 1 1522 elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM: 1523 return (1 << 4) - 1 1524 elif self.reader.arch == MD_CPU_ARCHITECTURE_X86: 1525 return (1 << 5) - 1 1526 1527 def PageAlignmentMask(self): 1528 return (1 << 20) - 1 1529 1530 1531 class KnownObject(HeapObject): 1532 def __init__(self, heap, known_name): 1533 HeapObject.__init__(self, heap, None, None) 1534 self.known_name = known_name 1535 1536 def __str__(self): 1537 return "<%s>" % self.known_name 1538 1539 1540 class KnownMap(HeapObject): 1541 def __init__(self, heap, known_name, instance_type): 1542 HeapObject.__init__(self, heap, None, None) 1543 self.instance_type = instance_type 1544 self.known_name = known_name 1545 1546 def __str__(self): 1547 return "<%s>" % self.known_name 1548 1549 1550 class InspectionPadawan(object): 1551 """The padawan can improve annotations by sensing well-known objects.""" 1552 def __init__(self, reader, heap): 1553 self.reader = reader 1554 self.heap = heap 1555 self.known_first_map_page = 0 1556 self.known_first_data_page = 0 1557 self.known_first_pointer_page = 0 1558 1559 def __getattr__(self, name): 1560 """An InspectionPadawan can be used instead of V8Heap, even though 1561 it does not inherit from V8Heap (aka. mixin).""" 1562 return getattr(self.heap, name) 1563 1564 def GetPageOffset(self, tagged_address): 1565 return tagged_address & self.heap.PageAlignmentMask() 1566 1567 def IsInKnownMapSpace(self, tagged_address): 1568 page_address = tagged_address & ~self.heap.PageAlignmentMask() 1569 return page_address == self.known_first_map_page 1570 1571 def IsInKnownOldSpace(self, tagged_address): 1572 page_address = tagged_address & ~self.heap.PageAlignmentMask() 1573 return page_address in [self.known_first_data_page, 1574 self.known_first_pointer_page] 1575 1576 def ContainingKnownOldSpaceName(self, tagged_address): 1577 page_address = tagged_address & ~self.heap.PageAlignmentMask() 1578 if page_address == self.known_first_data_page: return "OLD_DATA_SPACE" 1579 if page_address == self.known_first_pointer_page: return "OLD_POINTER_SPACE" 1580 return None 1581 1582 def SenseObject(self, tagged_address): 1583 if self.IsInKnownOldSpace(tagged_address): 1584 offset = self.GetPageOffset(tagged_address) 1585 lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset) 1586 known_obj_name = KNOWN_OBJECTS.get(lookup_key) 1587 if known_obj_name: 1588 return KnownObject(self, known_obj_name) 1589 if self.IsInKnownMapSpace(tagged_address): 1590 known_map = self.SenseMap(tagged_address) 1591 if known_map: 1592 return known_map 1593 found_obj = self.heap.FindObject(tagged_address) 1594 if found_obj: return found_obj 1595 address = tagged_address - 1 1596 if self.reader.IsValidAddress(address): 1597 map_tagged_address = self.reader.ReadUIntPtr(address) 1598 map = self.SenseMap(map_tagged_address) 1599 if map is None: return None 1600 instance_type_name = INSTANCE_TYPES.get(map.instance_type) 1601 if instance_type_name is None: return None 1602 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject) 1603 return cls(self, map, address) 1604 return None 1605 1606 def SenseMap(self, tagged_address): 1607 if self.IsInKnownMapSpace(tagged_address): 1608 offset = self.GetPageOffset(tagged_address) 1609 known_map_info = KNOWN_MAPS.get(offset) 1610 if known_map_info: 1611 known_map_type, known_map_name = known_map_info 1612 return KnownMap(self, known_map_name, known_map_type) 1613 found_map = self.heap.FindMap(tagged_address) 1614 if found_map: return found_map 1615 return None 1616 1617 def FindObjectOrSmi(self, tagged_address): 1618 """When used as a mixin in place of V8Heap.""" 1619 found_obj = self.SenseObject(tagged_address) 1620 if found_obj: return found_obj 1621 if (tagged_address & 1) == 0: 1622 return "Smi(%d)" % (tagged_address / 2) 1623 else: 1624 return "Unknown(%s)" % self.reader.FormatIntPtr(tagged_address) 1625 1626 def FindObject(self, tagged_address): 1627 """When used as a mixin in place of V8Heap.""" 1628 raise NotImplementedError 1629 1630 def FindMap(self, tagged_address): 1631 """When used as a mixin in place of V8Heap.""" 1632 raise NotImplementedError 1633 1634 def PrintKnowledge(self): 1635 print " known_first_map_page = %s\n"\ 1636 " known_first_data_page = %s\n"\ 1637 " known_first_pointer_page = %s" % ( 1638 self.reader.FormatIntPtr(self.known_first_map_page), 1639 self.reader.FormatIntPtr(self.known_first_data_page), 1640 self.reader.FormatIntPtr(self.known_first_pointer_page)) 1641 1642 1643 class InspectionShell(cmd.Cmd): 1644 def __init__(self, reader, heap): 1645 cmd.Cmd.__init__(self) 1646 self.reader = reader 1647 self.heap = heap 1648 self.padawan = InspectionPadawan(reader, heap) 1649 self.prompt = "(grok) " 1650 1651 def do_da(self, address): 1652 """ 1653 Print ASCII string starting at specified address. 1654 """ 1655 address = int(address, 16) 1656 string = "" 1657 while self.reader.IsValidAddress(address): 1658 code = self.reader.ReadU8(address) 1659 if code < 128: 1660 string += chr(code) 1661 else: 1662 break 1663 address += 1 1664 if string == "": 1665 print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address) 1666 else: 1667 print "%s\n" % string 1668 1669 def do_dd(self, address): 1670 """ 1671 Interpret memory at the given address (if available) as a sequence 1672 of words. Automatic alignment is not performed. 1673 """ 1674 start = int(address, 16) 1675 if (start & self.heap.ObjectAlignmentMask()) != 0: 1676 print "Warning: Dumping un-aligned memory, is this what you had in mind?" 1677 for slot in xrange(start, 1678 start + self.reader.PointerSize() * 10, 1679 self.reader.PointerSize()): 1680 if not self.reader.IsValidAddress(slot): 1681 print "Address is not contained within the minidump!" 1682 return 1683 maybe_address = self.reader.ReadUIntPtr(slot) 1684 heap_object = self.padawan.SenseObject(maybe_address) 1685 print "%s: %s %s" % (self.reader.FormatIntPtr(slot), 1686 self.reader.FormatIntPtr(maybe_address), 1687 heap_object or '') 1688 1689 def do_do(self, address): 1690 """ 1691 Interpret memory at the given address as a V8 object. Automatic 1692 alignment makes sure that you can pass tagged as well as un-tagged 1693 addresses. 1694 """ 1695 address = int(address, 16) 1696 if (address & self.heap.ObjectAlignmentMask()) == 0: 1697 address = address + 1 1698 elif (address & self.heap.ObjectAlignmentMask()) != 1: 1699 print "Address doesn't look like a valid pointer!" 1700 return 1701 heap_object = self.padawan.SenseObject(address) 1702 if heap_object: 1703 heap_object.Print(Printer()) 1704 else: 1705 print "Address cannot be interpreted as object!" 1706 1707 def do_do_desc(self, address): 1708 """ 1709 Print a descriptor array in a readable format. 1710 """ 1711 start = int(address, 16) 1712 if ((start & 1) == 1): start = start - 1 1713 DescriptorArray(FixedArray(self.heap, None, start)).Print(Printer()) 1714 1715 def do_do_map(self, address): 1716 """ 1717 Print a descriptor array in a readable format. 1718 """ 1719 start = int(address, 16) 1720 if ((start & 1) == 1): start = start - 1 1721 Map(self.heap, None, start).Print(Printer()) 1722 1723 def do_do_trans(self, address): 1724 """ 1725 Print a transition array in a readable format. 1726 """ 1727 start = int(address, 16) 1728 if ((start & 1) == 1): start = start - 1 1729 TransitionArray(FixedArray(self.heap, None, start)).Print(Printer()) 1730 1731 def do_dp(self, address): 1732 """ 1733 Interpret memory at the given address as being on a V8 heap page 1734 and print information about the page header (if available). 1735 """ 1736 address = int(address, 16) 1737 page_address = address & ~self.heap.PageAlignmentMask() 1738 if self.reader.IsValidAddress(page_address): 1739 raise NotImplementedError 1740 else: 1741 print "Page header is not available!" 1742 1743 def do_k(self, arguments): 1744 """ 1745 Teach V8 heap layout information to the inspector. This increases 1746 the amount of annotations the inspector can produce while dumping 1747 data. The first page of each heap space is of particular interest 1748 because it contains known objects that do not move. 1749 """ 1750 self.padawan.PrintKnowledge() 1751 1752 def do_kd(self, address): 1753 """ 1754 Teach V8 heap layout information to the inspector. Set the first 1755 data-space page by passing any pointer into that page. 1756 """ 1757 address = int(address, 16) 1758 page_address = address & ~self.heap.PageAlignmentMask() 1759 self.padawan.known_first_data_page = page_address 1760 1761 def do_km(self, address): 1762 """ 1763 Teach V8 heap layout information to the inspector. Set the first 1764 map-space page by passing any pointer into that page. 1765 """ 1766 address = int(address, 16) 1767 page_address = address & ~self.heap.PageAlignmentMask() 1768 self.padawan.known_first_map_page = page_address 1769 1770 def do_kp(self, address): 1771 """ 1772 Teach V8 heap layout information to the inspector. Set the first 1773 pointer-space page by passing any pointer into that page. 1774 """ 1775 address = int(address, 16) 1776 page_address = address & ~self.heap.PageAlignmentMask() 1777 self.padawan.known_first_pointer_page = page_address 1778 1779 def do_list(self, smth): 1780 """ 1781 List all available memory regions. 1782 """ 1783 def print_region(reader, start, size, location): 1784 print " %s - %s (%d bytes)" % (reader.FormatIntPtr(start), 1785 reader.FormatIntPtr(start + size), 1786 size) 1787 print "Available memory regions:" 1788 self.reader.ForEachMemoryRegion(print_region) 1789 1790 def do_lm(self, arg): 1791 """ 1792 List details for all loaded modules in the minidump. An argument can 1793 be passed to limit the output to only those modules that contain the 1794 argument as a substring (case insensitive match). 1795 """ 1796 for module in self.reader.module_list.modules: 1797 if arg: 1798 name = GetModuleName(self.reader, module).lower() 1799 if name.find(arg.lower()) >= 0: 1800 PrintModuleDetails(self.reader, module) 1801 else: 1802 PrintModuleDetails(self.reader, module) 1803 print 1804 1805 def do_s(self, word): 1806 """ 1807 Search for a given word in available memory regions. The given word 1808 is expanded to full pointer size and searched at aligned as well as 1809 un-aligned memory locations. Use 'sa' to search aligned locations 1810 only. 1811 """ 1812 try: 1813 word = int(word, 0) 1814 except ValueError: 1815 print "Malformed word, prefix with '0x' to use hexadecimal format." 1816 return 1817 print "Searching for word %d/0x%s:" % (word, self.reader.FormatIntPtr(word)) 1818 self.reader.FindWord(word) 1819 1820 def do_sh(self, none): 1821 """ 1822 Search for the V8 Heap object in all available memory regions. You 1823 might get lucky and find this rare treasure full of invaluable 1824 information. 1825 """ 1826 raise NotImplementedError 1827 1828 def do_u(self, args): 1829 """ 1830 Unassemble memory in the region [address, address + size). If the 1831 size is not specified, a default value of 32 bytes is used. 1832 Synopsis: u 0x<address> 0x<size> 1833 """ 1834 args = args.split(' ') 1835 start = int(args[0], 16) 1836 size = int(args[1], 16) if len(args) > 1 else 0x20 1837 if not self.reader.IsValidAddress(start): 1838 print "Address is not contained within the minidump!" 1839 return 1840 lines = self.reader.GetDisasmLines(start, size) 1841 for line in lines: 1842 print FormatDisasmLine(start, self.heap, line) 1843 print 1844 1845 def do_EOF(self, none): 1846 raise KeyboardInterrupt 1847 1848 EIP_PROXIMITY = 64 1849 1850 CONTEXT_FOR_ARCH = { 1851 MD_CPU_ARCHITECTURE_AMD64: 1852 ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip', 1853 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'], 1854 MD_CPU_ARCHITECTURE_ARM: 1855 ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 1856 'r10', 'r11', 'r12', 'sp', 'lr', 'pc'], 1857 MD_CPU_ARCHITECTURE_X86: 1858 ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip'] 1859 } 1860 1861 KNOWN_MODULES = {'chrome.exe', 'chrome.dll'} 1862 1863 def GetVersionString(ms, ls): 1864 return "%d.%d.%d.%d" % (ms >> 16, ms & 0xffff, ls >> 16, ls & 0xffff) 1865 1866 1867 def GetModuleName(reader, module): 1868 name = reader.ReadMinidumpString(module.module_name_rva) 1869 # simplify for path manipulation 1870 name = name.encode('utf-8') 1871 return str(os.path.basename(str(name).replace("\\", "/"))) 1872 1873 1874 def PrintModuleDetails(reader, module): 1875 print "%s" % GetModuleName(reader, module) 1876 file_version = GetVersionString(module.version_info.dwFileVersionMS, 1877 module.version_info.dwFileVersionLS) 1878 product_version = GetVersionString(module.version_info.dwProductVersionMS, 1879 module.version_info.dwProductVersionLS) 1880 print " base: %s" % reader.FormatIntPtr(module.base_of_image) 1881 print " end: %s" % reader.FormatIntPtr(module.base_of_image + 1882 module.size_of_image) 1883 print " file version: %s" % file_version 1884 print " product version: %s" % product_version 1885 time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp) 1886 print " timestamp: %s" % time_date_stamp 1887 1888 1889 def AnalyzeMinidump(options, minidump_name): 1890 reader = MinidumpReader(options, minidump_name) 1891 heap = None 1892 DebugPrint("========================================") 1893 if reader.exception is None: 1894 print "Minidump has no exception info" 1895 else: 1896 print "Exception info:" 1897 exception_thread = reader.thread_map[reader.exception.thread_id] 1898 print " thread id: %d" % exception_thread.id 1899 print " code: %08X" % reader.exception.exception.code 1900 print " context:" 1901 for r in CONTEXT_FOR_ARCH[reader.arch]: 1902 print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r))) 1903 # TODO(vitalyr): decode eflags. 1904 if reader.arch == MD_CPU_ARCHITECTURE_ARM: 1905 print " cpsr: %s" % bin(reader.exception_context.cpsr)[2:] 1906 else: 1907 print " eflags: %s" % bin(reader.exception_context.eflags)[2:] 1908 1909 print 1910 print " modules:" 1911 for module in reader.module_list.modules: 1912 name = GetModuleName(reader, module) 1913 if name in KNOWN_MODULES: 1914 print " %s at %08X" % (name, module.base_of_image) 1915 reader.TryLoadSymbolsFor(name, module) 1916 print 1917 1918 stack_top = reader.ExceptionSP() 1919 stack_bottom = exception_thread.stack.start + \ 1920 exception_thread.stack.memory.data_size 1921 stack_map = {reader.ExceptionIP(): -1} 1922 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): 1923 maybe_address = reader.ReadUIntPtr(slot) 1924 if not maybe_address in stack_map: 1925 stack_map[maybe_address] = slot 1926 heap = V8Heap(reader, stack_map) 1927 1928 print "Disassembly around exception.eip:" 1929 eip_symbol = reader.FindSymbol(reader.ExceptionIP()) 1930 if eip_symbol is not None: 1931 print eip_symbol 1932 disasm_start = reader.ExceptionIP() - EIP_PROXIMITY 1933 disasm_bytes = 2 * EIP_PROXIMITY 1934 if (options.full): 1935 full_range = reader.FindRegion(reader.ExceptionIP()) 1936 if full_range is not None: 1937 disasm_start = full_range[0] 1938 disasm_bytes = full_range[1] 1939 1940 lines = reader.GetDisasmLines(disasm_start, disasm_bytes) 1941 1942 for line in lines: 1943 print FormatDisasmLine(disasm_start, heap, line) 1944 print 1945 1946 if heap is None: 1947 heap = V8Heap(reader, None) 1948 1949 if options.full: 1950 FullDump(reader, heap) 1951 1952 if options.command: 1953 InspectionShell(reader, heap).onecmd(options.command) 1954 1955 if options.shell: 1956 try: 1957 InspectionShell(reader, heap).cmdloop("type help to get help") 1958 except KeyboardInterrupt: 1959 print "Kthxbye." 1960 elif not options.command: 1961 if reader.exception is not None: 1962 frame_pointer = reader.ExceptionFP() 1963 print "Annotated stack (from exception.esp to bottom):" 1964 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): 1965 maybe_address = reader.ReadUIntPtr(slot) 1966 heap_object = heap.FindObject(maybe_address) 1967 maybe_symbol = reader.FindSymbol(maybe_address) 1968 if slot == frame_pointer: 1969 maybe_symbol = "<---- frame pointer" 1970 frame_pointer = maybe_address 1971 print "%s: %s %s" % (reader.FormatIntPtr(slot), 1972 reader.FormatIntPtr(maybe_address), 1973 maybe_symbol or "") 1974 if heap_object: 1975 heap_object.Print(Printer()) 1976 print 1977 1978 reader.Dispose() 1979 1980 1981 if __name__ == "__main__": 1982 parser = optparse.OptionParser(USAGE) 1983 parser.add_option("-s", "--shell", dest="shell", action="store_true", 1984 help="start an interactive inspector shell") 1985 parser.add_option("-c", "--command", dest="command", default="", 1986 help="run an interactive inspector shell command and exit") 1987 parser.add_option("-f", "--full", dest="full", action="store_true", 1988 help="dump all information contained in the minidump") 1989 parser.add_option("--symdir", dest="symdir", default=".", 1990 help="directory containing *.pdb.sym file with symbols") 1991 parser.add_option("--objdump", 1992 default="/usr/bin/objdump", 1993 help="objdump tool to use [default: %default]") 1994 options, args = parser.parse_args() 1995 if os.path.exists(options.objdump): 1996 disasm.OBJDUMP_BIN = options.objdump 1997 OBJDUMP_BIN = options.objdump 1998 else: 1999 print "Cannot find %s, falling back to default objdump" % options.objdump 2000 if len(args) != 1: 2001 parser.print_help() 2002 sys.exit(1) 2003 AnalyzeMinidump(options, args[0]) 2004