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 # First word after the map is the hash, the second is the length. 1033 return self.heap.PointerSize() * 2 1034 1035 def __init__(self, heap, map, address): 1036 HeapObject.__init__(self, heap, map, address) 1037 self.length = self.SmiField(self.LengthOffset()) 1038 1039 def GetChars(self): 1040 return "?string?" 1041 1042 def Print(self, p): 1043 p.Print(str(self)) 1044 1045 def __str__(self): 1046 return "\"%s\"" % self.GetChars() 1047 1048 1049 class SeqString(String): 1050 def CharsOffset(self): 1051 return self.heap.PointerSize() * 3 1052 1053 def __init__(self, heap, map, address): 1054 String.__init__(self, heap, map, address) 1055 self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(), 1056 self.length) 1057 1058 def GetChars(self): 1059 return self.chars 1060 1061 1062 class ExternalString(String): 1063 # TODO(vegorov) fix ExternalString for X64 architecture 1064 RESOURCE_OFFSET = 12 1065 1066 WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4 1067 WEBKIT_STRING_IMPL_CHARS_OFFSET = 8 1068 1069 def __init__(self, heap, map, address): 1070 String.__init__(self, heap, map, address) 1071 reader = heap.reader 1072 self.resource = \ 1073 reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET) 1074 self.chars = "?external string?" 1075 if not reader.IsValidAddress(self.resource): return 1076 string_impl_address = self.resource + \ 1077 ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET 1078 if not reader.IsValidAddress(string_impl_address): return 1079 string_impl = reader.ReadU32(string_impl_address) 1080 chars_ptr_address = string_impl + \ 1081 ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET 1082 if not reader.IsValidAddress(chars_ptr_address): return 1083 chars_ptr = reader.ReadU32(chars_ptr_address) 1084 if not reader.IsValidAddress(chars_ptr): return 1085 raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length) 1086 self.chars = codecs.getdecoder("utf16")(raw_chars)[0] 1087 1088 def GetChars(self): 1089 return self.chars 1090 1091 1092 class ConsString(String): 1093 def LeftOffset(self): 1094 return self.heap.PointerSize() * 3 1095 1096 def RightOffset(self): 1097 return self.heap.PointerSize() * 4 1098 1099 def __init__(self, heap, map, address): 1100 String.__init__(self, heap, map, address) 1101 self.left = self.ObjectField(self.LeftOffset()) 1102 self.right = self.ObjectField(self.RightOffset()) 1103 1104 def GetChars(self): 1105 try: 1106 return self.left.GetChars() + self.right.GetChars() 1107 except: 1108 return "***CAUGHT EXCEPTION IN GROKDUMP***" 1109 1110 1111 class Oddball(HeapObject): 1112 # Should match declarations in objects.h 1113 KINDS = [ 1114 "False", 1115 "True", 1116 "TheHole", 1117 "Null", 1118 "ArgumentMarker", 1119 "Undefined", 1120 "Other" 1121 ] 1122 1123 def ToStringOffset(self): 1124 return self.heap.PointerSize() 1125 1126 def ToNumberOffset(self): 1127 return self.ToStringOffset() + self.heap.PointerSize() 1128 1129 def KindOffset(self): 1130 return self.ToNumberOffset() + self.heap.PointerSize() 1131 1132 def __init__(self, heap, map, address): 1133 HeapObject.__init__(self, heap, map, address) 1134 self.to_string = self.ObjectField(self.ToStringOffset()) 1135 self.kind = self.SmiField(self.KindOffset()) 1136 1137 def Print(self, p): 1138 p.Print(str(self)) 1139 1140 def __str__(self): 1141 if self.to_string: 1142 return "Oddball(%08x, <%s>)" % (self.address, str(self.to_string)) 1143 else: 1144 kind = "???" 1145 if 0 <= self.kind < len(Oddball.KINDS): 1146 kind = Oddball.KINDS[self.kind] 1147 return "Oddball(%08x, kind=%s)" % (self.address, kind) 1148 1149 1150 class FixedArray(HeapObject): 1151 def LengthOffset(self): 1152 return self.heap.PointerSize() 1153 1154 def ElementsOffset(self): 1155 return self.heap.PointerSize() * 2 1156 1157 def MemberOffset(self, i): 1158 return self.ElementsOffset() + self.heap.PointerSize() * i 1159 1160 def Get(self, i): 1161 return self.ObjectField(self.MemberOffset(i)) 1162 1163 def __init__(self, heap, map, address): 1164 HeapObject.__init__(self, heap, map, address) 1165 self.length = self.SmiField(self.LengthOffset()) 1166 1167 def Print(self, p): 1168 p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address)) 1169 p.Indent() 1170 p.Print("length: %d" % self.length) 1171 base_offset = self.ElementsOffset() 1172 for i in xrange(self.length): 1173 offset = base_offset + 4 * i 1174 try: 1175 p.Print("[%08d] = %s" % (i, self.ObjectField(offset))) 1176 except TypeError: 1177 p.Dedent() 1178 p.Print("...") 1179 p.Print("}") 1180 return 1181 p.Dedent() 1182 p.Print("}") 1183 1184 def __str__(self): 1185 return "FixedArray(%08x, length=%d)" % (self.address, self.length) 1186 1187 1188 class DescriptorArray(object): 1189 def __init__(self, array): 1190 self.array = array 1191 1192 def Length(self): 1193 return self.array.Get(0) 1194 1195 def Decode(self, offset, size, value): 1196 return (value >> offset) & ((1 << size) - 1) 1197 1198 TYPES = [ 1199 "normal", 1200 "field", 1201 "function", 1202 "callbacks" 1203 ] 1204 1205 def Type(self, value): 1206 return DescriptorArray.TYPES[self.Decode(0, 3, value)] 1207 1208 def Attributes(self, value): 1209 attributes = self.Decode(3, 3, value) 1210 result = [] 1211 if (attributes & 0): result += ["ReadOnly"] 1212 if (attributes & 1): result += ["DontEnum"] 1213 if (attributes & 2): result += ["DontDelete"] 1214 return "[" + (",".join(result)) + "]" 1215 1216 def Deleted(self, value): 1217 return self.Decode(6, 1, value) == 1 1218 1219 def FieldIndex(self, value): 1220 return self.Decode(20, 11, value) 1221 1222 def Pointer(self, value): 1223 return self.Decode(6, 11, value) 1224 1225 def Details(self, di, value): 1226 return ( 1227 di, 1228 self.Type(value), 1229 self.Attributes(value), 1230 self.FieldIndex(value), 1231 self.Pointer(value) 1232 ) 1233 1234 1235 def Print(self, p): 1236 length = self.Length() 1237 array = self.array 1238 1239 p.Print("Descriptors(%08x, length=%d)" % (array.address, length)) 1240 p.Print("[et] %s" % (array.Get(1))) 1241 1242 for di in xrange(length): 1243 i = 2 + di * 3 1244 p.Print("0x%x" % (array.address + array.MemberOffset(i))) 1245 p.Print("[%i] name: %s" % (di, array.Get(i + 0))) 1246 p.Print("[%i] details: %s %s field-index %i pointer %i" % \ 1247 self.Details(di, array.Get(i + 1))) 1248 p.Print("[%i] value: %s" % (di, array.Get(i + 2))) 1249 1250 end = self.array.length // 3 1251 if length != end: 1252 p.Print("[%i-%i] slack descriptors" % (length, end)) 1253 1254 1255 class TransitionArray(object): 1256 def __init__(self, array): 1257 self.array = array 1258 1259 def IsSimpleTransition(self): 1260 return self.array.length <= 2 1261 1262 def Length(self): 1263 # SimpleTransition cases 1264 if self.IsSimpleTransition(): 1265 return self.array.length - 1 1266 return (self.array.length - 3) // 2 1267 1268 def Print(self, p): 1269 length = self.Length() 1270 array = self.array 1271 1272 p.Print("Transitions(%08x, length=%d)" % (array.address, length)) 1273 p.Print("[backpointer] %s" % (array.Get(0))) 1274 if self.IsSimpleTransition(): 1275 if length == 1: 1276 p.Print("[simple target] %s" % (array.Get(1))) 1277 return 1278 1279 elements = array.Get(1) 1280 if elements is not None: 1281 p.Print("[elements ] %s" % (elements)) 1282 1283 prototype = array.Get(2) 1284 if prototype is not None: 1285 p.Print("[prototype ] %s" % (prototype)) 1286 1287 for di in xrange(length): 1288 i = 3 + di * 2 1289 p.Print("[%i] symbol: %s" % (di, array.Get(i + 0))) 1290 p.Print("[%i] target: %s" % (di, array.Get(i + 1))) 1291 1292 1293 class JSFunction(HeapObject): 1294 def CodeEntryOffset(self): 1295 return 3 * self.heap.PointerSize() 1296 1297 def SharedOffset(self): 1298 return 5 * self.heap.PointerSize() 1299 1300 def __init__(self, heap, map, address): 1301 HeapObject.__init__(self, heap, map, address) 1302 code_entry = \ 1303 heap.reader.ReadU32(self.address + self.CodeEntryOffset()) 1304 self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1) 1305 self.shared = self.ObjectField(self.SharedOffset()) 1306 1307 def Print(self, p): 1308 source = "\n".join(" %s" % line for line in self._GetSource().split("\n")) 1309 p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address)) 1310 p.Indent() 1311 p.Print("inferred name: %s" % self.shared.inferred_name) 1312 if self.shared.script.Is(Script) and self.shared.script.name.Is(String): 1313 p.Print("script name: %s" % self.shared.script.name) 1314 p.Print("source:") 1315 p.PrintLines(self._GetSource().split("\n")) 1316 p.Print("code:") 1317 self.code.Print(p) 1318 if self.code != self.shared.code: 1319 p.Print("unoptimized code:") 1320 self.shared.code.Print(p) 1321 p.Dedent() 1322 p.Print("}") 1323 1324 def __str__(self): 1325 inferred_name = "" 1326 if self.shared.Is(SharedFunctionInfo): 1327 inferred_name = self.shared.inferred_name 1328 return "JSFunction(%s, %s)" % \ 1329 (self.heap.reader.FormatIntPtr(self.address), inferred_name) 1330 1331 def _GetSource(self): 1332 source = "?source?" 1333 start = self.shared.start_position 1334 end = self.shared.end_position 1335 if not self.shared.script.Is(Script): return source 1336 script_source = self.shared.script.source 1337 if not script_source.Is(String): return source 1338 return script_source.GetChars()[start:end] 1339 1340 1341 class SharedFunctionInfo(HeapObject): 1342 def CodeOffset(self): 1343 return 2 * self.heap.PointerSize() 1344 1345 def ScriptOffset(self): 1346 return 7 * self.heap.PointerSize() 1347 1348 def InferredNameOffset(self): 1349 return 9 * self.heap.PointerSize() 1350 1351 def EndPositionOffset(self): 1352 return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize() 1353 1354 def StartPositionAndTypeOffset(self): 1355 return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize() 1356 1357 def __init__(self, heap, map, address): 1358 HeapObject.__init__(self, heap, map, address) 1359 self.code = self.ObjectField(self.CodeOffset()) 1360 self.script = self.ObjectField(self.ScriptOffset()) 1361 self.inferred_name = self.ObjectField(self.InferredNameOffset()) 1362 if heap.PointerSize() == 8: 1363 start_position_and_type = \ 1364 heap.reader.ReadU32(self.StartPositionAndTypeOffset()) 1365 self.start_position = start_position_and_type >> 2 1366 pseudo_smi_end_position = \ 1367 heap.reader.ReadU32(self.EndPositionOffset()) 1368 self.end_position = pseudo_smi_end_position >> 2 1369 else: 1370 start_position_and_type = \ 1371 self.SmiField(self.StartPositionAndTypeOffset()) 1372 self.start_position = start_position_and_type >> 2 1373 self.end_position = \ 1374 self.SmiField(self.EndPositionOffset()) 1375 1376 1377 class Script(HeapObject): 1378 def SourceOffset(self): 1379 return self.heap.PointerSize() 1380 1381 def NameOffset(self): 1382 return self.SourceOffset() + self.heap.PointerSize() 1383 1384 def __init__(self, heap, map, address): 1385 HeapObject.__init__(self, heap, map, address) 1386 self.source = self.ObjectField(self.SourceOffset()) 1387 self.name = self.ObjectField(self.NameOffset()) 1388 1389 1390 class CodeCache(HeapObject): 1391 def DefaultCacheOffset(self): 1392 return self.heap.PointerSize() 1393 1394 def NormalTypeCacheOffset(self): 1395 return self.DefaultCacheOffset() + self.heap.PointerSize() 1396 1397 def __init__(self, heap, map, address): 1398 HeapObject.__init__(self, heap, map, address) 1399 self.default_cache = self.ObjectField(self.DefaultCacheOffset()) 1400 self.normal_type_cache = self.ObjectField(self.NormalTypeCacheOffset()) 1401 1402 def Print(self, p): 1403 p.Print("CodeCache(%s) {" % self.heap.reader.FormatIntPtr(self.address)) 1404 p.Indent() 1405 p.Print("default cache: %s" % self.default_cache) 1406 p.Print("normal type cache: %s" % self.normal_type_cache) 1407 p.Dedent() 1408 p.Print("}") 1409 1410 1411 class Code(HeapObject): 1412 CODE_ALIGNMENT_MASK = (1 << 5) - 1 1413 1414 def InstructionSizeOffset(self): 1415 return self.heap.PointerSize() 1416 1417 @staticmethod 1418 def HeaderSize(heap): 1419 return (heap.PointerSize() + heap.IntSize() + \ 1420 4 * heap.PointerSize() + 3 * heap.IntSize() + \ 1421 Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK 1422 1423 def __init__(self, heap, map, address): 1424 HeapObject.__init__(self, heap, map, address) 1425 self.entry = self.address + Code.HeaderSize(heap) 1426 self.instruction_size = \ 1427 heap.reader.ReadU32(self.address + self.InstructionSizeOffset()) 1428 1429 def Print(self, p): 1430 lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size) 1431 p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address)) 1432 p.Indent() 1433 p.Print("instruction_size: %d" % self.instruction_size) 1434 p.PrintLines(self._FormatLine(line) for line in lines) 1435 p.Dedent() 1436 p.Print("}") 1437 1438 def _FormatLine(self, line): 1439 return FormatDisasmLine(self.entry, self.heap, line) 1440 1441 1442 class V8Heap(object): 1443 CLASS_MAP = { 1444 "SYMBOL_TYPE": SeqString, 1445 "ASCII_SYMBOL_TYPE": SeqString, 1446 "CONS_SYMBOL_TYPE": ConsString, 1447 "CONS_ASCII_SYMBOL_TYPE": ConsString, 1448 "EXTERNAL_SYMBOL_TYPE": ExternalString, 1449 "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString, 1450 "EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString, 1451 "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString, 1452 "SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString, 1453 "SHORT_EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString, 1454 "STRING_TYPE": SeqString, 1455 "ASCII_STRING_TYPE": SeqString, 1456 "CONS_STRING_TYPE": ConsString, 1457 "CONS_ASCII_STRING_TYPE": ConsString, 1458 "EXTERNAL_STRING_TYPE": ExternalString, 1459 "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE": ExternalString, 1460 "EXTERNAL_ASCII_STRING_TYPE": ExternalString, 1461 "MAP_TYPE": Map, 1462 "ODDBALL_TYPE": Oddball, 1463 "FIXED_ARRAY_TYPE": FixedArray, 1464 "JS_FUNCTION_TYPE": JSFunction, 1465 "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo, 1466 "SCRIPT_TYPE": Script, 1467 "CODE_CACHE_TYPE": CodeCache, 1468 "CODE_TYPE": Code, 1469 } 1470 1471 def __init__(self, reader, stack_map): 1472 self.reader = reader 1473 self.stack_map = stack_map 1474 self.objects = {} 1475 1476 def FindObjectOrSmi(self, tagged_address): 1477 if (tagged_address & 1) == 0: return tagged_address / 2 1478 return self.FindObject(tagged_address) 1479 1480 def FindObject(self, tagged_address): 1481 if tagged_address in self.objects: 1482 return self.objects[tagged_address] 1483 if (tagged_address & self.ObjectAlignmentMask()) != 1: return None 1484 address = tagged_address - 1 1485 if not self.reader.IsValidAddress(address): return None 1486 map_tagged_address = self.reader.ReadUIntPtr(address) 1487 if tagged_address == map_tagged_address: 1488 # Meta map? 1489 meta_map = Map(self, None, address) 1490 instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type) 1491 if instance_type_name != "MAP_TYPE": return None 1492 meta_map.map = meta_map 1493 object = meta_map 1494 else: 1495 map = self.FindMap(map_tagged_address) 1496 if map is None: return None 1497 instance_type_name = INSTANCE_TYPES.get(map.instance_type) 1498 if instance_type_name is None: return None 1499 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject) 1500 object = cls(self, map, address) 1501 self.objects[tagged_address] = object 1502 return object 1503 1504 def FindMap(self, tagged_address): 1505 if (tagged_address & self.MapAlignmentMask()) != 1: return None 1506 address = tagged_address - 1 1507 if not self.reader.IsValidAddress(address): return None 1508 object = Map(self, None, address) 1509 return object 1510 1511 def IntSize(self): 1512 return 4 1513 1514 def PointerSize(self): 1515 return self.reader.PointerSize() 1516 1517 def ObjectAlignmentMask(self): 1518 return self.PointerSize() - 1 1519 1520 def MapAlignmentMask(self): 1521 if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64: 1522 return (1 << 4) - 1 1523 elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM: 1524 return (1 << 4) - 1 1525 elif self.reader.arch == MD_CPU_ARCHITECTURE_X86: 1526 return (1 << 5) - 1 1527 1528 def PageAlignmentMask(self): 1529 return (1 << 20) - 1 1530 1531 1532 class KnownObject(HeapObject): 1533 def __init__(self, heap, known_name): 1534 HeapObject.__init__(self, heap, None, None) 1535 self.known_name = known_name 1536 1537 def __str__(self): 1538 return "<%s>" % self.known_name 1539 1540 1541 class KnownMap(HeapObject): 1542 def __init__(self, heap, known_name, instance_type): 1543 HeapObject.__init__(self, heap, None, None) 1544 self.instance_type = instance_type 1545 self.known_name = known_name 1546 1547 def __str__(self): 1548 return "<%s>" % self.known_name 1549 1550 1551 class InspectionPadawan(object): 1552 """The padawan can improve annotations by sensing well-known objects.""" 1553 def __init__(self, reader, heap): 1554 self.reader = reader 1555 self.heap = heap 1556 self.known_first_map_page = 0 1557 self.known_first_data_page = 0 1558 self.known_first_pointer_page = 0 1559 1560 def __getattr__(self, name): 1561 """An InspectionPadawan can be used instead of V8Heap, even though 1562 it does not inherit from V8Heap (aka. mixin).""" 1563 return getattr(self.heap, name) 1564 1565 def GetPageOffset(self, tagged_address): 1566 return tagged_address & self.heap.PageAlignmentMask() 1567 1568 def IsInKnownMapSpace(self, tagged_address): 1569 page_address = tagged_address & ~self.heap.PageAlignmentMask() 1570 return page_address == self.known_first_map_page 1571 1572 def IsInKnownOldSpace(self, tagged_address): 1573 page_address = tagged_address & ~self.heap.PageAlignmentMask() 1574 return page_address in [self.known_first_data_page, 1575 self.known_first_pointer_page] 1576 1577 def ContainingKnownOldSpaceName(self, tagged_address): 1578 page_address = tagged_address & ~self.heap.PageAlignmentMask() 1579 if page_address == self.known_first_data_page: return "OLD_DATA_SPACE" 1580 if page_address == self.known_first_pointer_page: return "OLD_POINTER_SPACE" 1581 return None 1582 1583 def SenseObject(self, tagged_address): 1584 if self.IsInKnownOldSpace(tagged_address): 1585 offset = self.GetPageOffset(tagged_address) 1586 lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset) 1587 known_obj_name = KNOWN_OBJECTS.get(lookup_key) 1588 if known_obj_name: 1589 return KnownObject(self, known_obj_name) 1590 if self.IsInKnownMapSpace(tagged_address): 1591 known_map = self.SenseMap(tagged_address) 1592 if known_map: 1593 return known_map 1594 found_obj = self.heap.FindObject(tagged_address) 1595 if found_obj: return found_obj 1596 address = tagged_address - 1 1597 if self.reader.IsValidAddress(address): 1598 map_tagged_address = self.reader.ReadUIntPtr(address) 1599 map = self.SenseMap(map_tagged_address) 1600 if map is None: return None 1601 instance_type_name = INSTANCE_TYPES.get(map.instance_type) 1602 if instance_type_name is None: return None 1603 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject) 1604 return cls(self, map, address) 1605 return None 1606 1607 def SenseMap(self, tagged_address): 1608 if self.IsInKnownMapSpace(tagged_address): 1609 offset = self.GetPageOffset(tagged_address) 1610 known_map_info = KNOWN_MAPS.get(offset) 1611 if known_map_info: 1612 known_map_type, known_map_name = known_map_info 1613 return KnownMap(self, known_map_name, known_map_type) 1614 found_map = self.heap.FindMap(tagged_address) 1615 if found_map: return found_map 1616 return None 1617 1618 def FindObjectOrSmi(self, tagged_address): 1619 """When used as a mixin in place of V8Heap.""" 1620 found_obj = self.SenseObject(tagged_address) 1621 if found_obj: return found_obj 1622 if (tagged_address & 1) == 0: 1623 return "Smi(%d)" % (tagged_address / 2) 1624 else: 1625 return "Unknown(%s)" % self.reader.FormatIntPtr(tagged_address) 1626 1627 def FindObject(self, tagged_address): 1628 """When used as a mixin in place of V8Heap.""" 1629 raise NotImplementedError 1630 1631 def FindMap(self, tagged_address): 1632 """When used as a mixin in place of V8Heap.""" 1633 raise NotImplementedError 1634 1635 def PrintKnowledge(self): 1636 print " known_first_map_page = %s\n"\ 1637 " known_first_data_page = %s\n"\ 1638 " known_first_pointer_page = %s" % ( 1639 self.reader.FormatIntPtr(self.known_first_map_page), 1640 self.reader.FormatIntPtr(self.known_first_data_page), 1641 self.reader.FormatIntPtr(self.known_first_pointer_page)) 1642 1643 1644 class InspectionShell(cmd.Cmd): 1645 def __init__(self, reader, heap): 1646 cmd.Cmd.__init__(self) 1647 self.reader = reader 1648 self.heap = heap 1649 self.padawan = InspectionPadawan(reader, heap) 1650 self.prompt = "(grok) " 1651 1652 def do_da(self, address): 1653 """ 1654 Print ASCII string starting at specified address. 1655 """ 1656 address = int(address, 16) 1657 string = "" 1658 while self.reader.IsValidAddress(address): 1659 code = self.reader.ReadU8(address) 1660 if code < 128: 1661 string += chr(code) 1662 else: 1663 break 1664 address += 1 1665 if string == "": 1666 print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address) 1667 else: 1668 print "%s\n" % string 1669 1670 def do_dd(self, address): 1671 """ 1672 Interpret memory at the given address (if available) as a sequence 1673 of words. Automatic alignment is not performed. 1674 """ 1675 start = int(address, 16) 1676 if (start & self.heap.ObjectAlignmentMask()) != 0: 1677 print "Warning: Dumping un-aligned memory, is this what you had in mind?" 1678 for slot in xrange(start, 1679 start + self.reader.PointerSize() * 10, 1680 self.reader.PointerSize()): 1681 if not self.reader.IsValidAddress(slot): 1682 print "Address is not contained within the minidump!" 1683 return 1684 maybe_address = self.reader.ReadUIntPtr(slot) 1685 heap_object = self.padawan.SenseObject(maybe_address) 1686 print "%s: %s %s" % (self.reader.FormatIntPtr(slot), 1687 self.reader.FormatIntPtr(maybe_address), 1688 heap_object or '') 1689 1690 def do_do(self, address): 1691 """ 1692 Interpret memory at the given address as a V8 object. Automatic 1693 alignment makes sure that you can pass tagged as well as un-tagged 1694 addresses. 1695 """ 1696 address = int(address, 16) 1697 if (address & self.heap.ObjectAlignmentMask()) == 0: 1698 address = address + 1 1699 elif (address & self.heap.ObjectAlignmentMask()) != 1: 1700 print "Address doesn't look like a valid pointer!" 1701 return 1702 heap_object = self.padawan.SenseObject(address) 1703 if heap_object: 1704 heap_object.Print(Printer()) 1705 else: 1706 print "Address cannot be interpreted as object!" 1707 1708 def do_do_desc(self, address): 1709 """ 1710 Print a descriptor array in a readable format. 1711 """ 1712 start = int(address, 16) 1713 if ((start & 1) == 1): start = start - 1 1714 DescriptorArray(FixedArray(self.heap, None, start)).Print(Printer()) 1715 1716 def do_do_map(self, address): 1717 """ 1718 Print a descriptor array in a readable format. 1719 """ 1720 start = int(address, 16) 1721 if ((start & 1) == 1): start = start - 1 1722 Map(self.heap, None, start).Print(Printer()) 1723 1724 def do_do_trans(self, address): 1725 """ 1726 Print a transition array in a readable format. 1727 """ 1728 start = int(address, 16) 1729 if ((start & 1) == 1): start = start - 1 1730 TransitionArray(FixedArray(self.heap, None, start)).Print(Printer()) 1731 1732 def do_dp(self, address): 1733 """ 1734 Interpret memory at the given address as being on a V8 heap page 1735 and print information about the page header (if available). 1736 """ 1737 address = int(address, 16) 1738 page_address = address & ~self.heap.PageAlignmentMask() 1739 if self.reader.IsValidAddress(page_address): 1740 raise NotImplementedError 1741 else: 1742 print "Page header is not available!" 1743 1744 def do_k(self, arguments): 1745 """ 1746 Teach V8 heap layout information to the inspector. This increases 1747 the amount of annotations the inspector can produce while dumping 1748 data. The first page of each heap space is of particular interest 1749 because it contains known objects that do not move. 1750 """ 1751 self.padawan.PrintKnowledge() 1752 1753 def do_kd(self, address): 1754 """ 1755 Teach V8 heap layout information to the inspector. Set the first 1756 data-space page by passing any pointer into that page. 1757 """ 1758 address = int(address, 16) 1759 page_address = address & ~self.heap.PageAlignmentMask() 1760 self.padawan.known_first_data_page = page_address 1761 1762 def do_km(self, address): 1763 """ 1764 Teach V8 heap layout information to the inspector. Set the first 1765 map-space page by passing any pointer into that page. 1766 """ 1767 address = int(address, 16) 1768 page_address = address & ~self.heap.PageAlignmentMask() 1769 self.padawan.known_first_map_page = page_address 1770 1771 def do_kp(self, address): 1772 """ 1773 Teach V8 heap layout information to the inspector. Set the first 1774 pointer-space page by passing any pointer into that page. 1775 """ 1776 address = int(address, 16) 1777 page_address = address & ~self.heap.PageAlignmentMask() 1778 self.padawan.known_first_pointer_page = page_address 1779 1780 def do_list(self, smth): 1781 """ 1782 List all available memory regions. 1783 """ 1784 def print_region(reader, start, size, location): 1785 print " %s - %s (%d bytes)" % (reader.FormatIntPtr(start), 1786 reader.FormatIntPtr(start + size), 1787 size) 1788 print "Available memory regions:" 1789 self.reader.ForEachMemoryRegion(print_region) 1790 1791 def do_lm(self, arg): 1792 """ 1793 List details for all loaded modules in the minidump. An argument can 1794 be passed to limit the output to only those modules that contain the 1795 argument as a substring (case insensitive match). 1796 """ 1797 for module in self.reader.module_list.modules: 1798 if arg: 1799 name = GetModuleName(self.reader, module).lower() 1800 if name.find(arg.lower()) >= 0: 1801 PrintModuleDetails(self.reader, module) 1802 else: 1803 PrintModuleDetails(self.reader, module) 1804 print 1805 1806 def do_s(self, word): 1807 """ 1808 Search for a given word in available memory regions. The given word 1809 is expanded to full pointer size and searched at aligned as well as 1810 un-aligned memory locations. Use 'sa' to search aligned locations 1811 only. 1812 """ 1813 try: 1814 word = int(word, 0) 1815 except ValueError: 1816 print "Malformed word, prefix with '0x' to use hexadecimal format." 1817 return 1818 print "Searching for word %d/0x%s:" % (word, self.reader.FormatIntPtr(word)) 1819 self.reader.FindWord(word) 1820 1821 def do_sh(self, none): 1822 """ 1823 Search for the V8 Heap object in all available memory regions. You 1824 might get lucky and find this rare treasure full of invaluable 1825 information. 1826 """ 1827 raise NotImplementedError 1828 1829 def do_u(self, args): 1830 """ 1831 Unassemble memory in the region [address, address + size). If the 1832 size is not specified, a default value of 32 bytes is used. 1833 Synopsis: u 0x<address> 0x<size> 1834 """ 1835 args = args.split(' ') 1836 start = int(args[0], 16) 1837 size = int(args[1], 16) if len(args) > 1 else 0x20 1838 if not self.reader.IsValidAddress(start): 1839 print "Address is not contained within the minidump!" 1840 return 1841 lines = self.reader.GetDisasmLines(start, size) 1842 for line in lines: 1843 print FormatDisasmLine(start, self.heap, line) 1844 print 1845 1846 def do_EOF(self, none): 1847 raise KeyboardInterrupt 1848 1849 EIP_PROXIMITY = 64 1850 1851 CONTEXT_FOR_ARCH = { 1852 MD_CPU_ARCHITECTURE_AMD64: 1853 ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip', 1854 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'], 1855 MD_CPU_ARCHITECTURE_ARM: 1856 ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 1857 'r10', 'r11', 'r12', 'sp', 'lr', 'pc'], 1858 MD_CPU_ARCHITECTURE_X86: 1859 ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip'] 1860 } 1861 1862 KNOWN_MODULES = {'chrome.exe', 'chrome.dll'} 1863 1864 def GetVersionString(ms, ls): 1865 return "%d.%d.%d.%d" % (ms >> 16, ms & 0xffff, ls >> 16, ls & 0xffff) 1866 1867 1868 def GetModuleName(reader, module): 1869 name = reader.ReadMinidumpString(module.module_name_rva) 1870 # simplify for path manipulation 1871 name = name.encode('utf-8') 1872 return str(os.path.basename(str(name).replace("\\", "/"))) 1873 1874 1875 def PrintModuleDetails(reader, module): 1876 print "%s" % GetModuleName(reader, module) 1877 file_version = GetVersionString(module.version_info.dwFileVersionMS, 1878 module.version_info.dwFileVersionLS) 1879 product_version = GetVersionString(module.version_info.dwProductVersionMS, 1880 module.version_info.dwProductVersionLS) 1881 print " base: %s" % reader.FormatIntPtr(module.base_of_image) 1882 print " end: %s" % reader.FormatIntPtr(module.base_of_image + 1883 module.size_of_image) 1884 print " file version: %s" % file_version 1885 print " product version: %s" % product_version 1886 time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp) 1887 print " timestamp: %s" % time_date_stamp 1888 1889 1890 def AnalyzeMinidump(options, minidump_name): 1891 reader = MinidumpReader(options, minidump_name) 1892 heap = None 1893 DebugPrint("========================================") 1894 if reader.exception is None: 1895 print "Minidump has no exception info" 1896 else: 1897 print "Exception info:" 1898 exception_thread = reader.thread_map[reader.exception.thread_id] 1899 print " thread id: %d" % exception_thread.id 1900 print " code: %08X" % reader.exception.exception.code 1901 print " context:" 1902 for r in CONTEXT_FOR_ARCH[reader.arch]: 1903 print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r))) 1904 # TODO(vitalyr): decode eflags. 1905 if reader.arch == MD_CPU_ARCHITECTURE_ARM: 1906 print " cpsr: %s" % bin(reader.exception_context.cpsr)[2:] 1907 else: 1908 print " eflags: %s" % bin(reader.exception_context.eflags)[2:] 1909 1910 print 1911 print " modules:" 1912 for module in reader.module_list.modules: 1913 name = GetModuleName(reader, module) 1914 if name in KNOWN_MODULES: 1915 print " %s at %08X" % (name, module.base_of_image) 1916 reader.TryLoadSymbolsFor(name, module) 1917 print 1918 1919 stack_top = reader.ExceptionSP() 1920 stack_bottom = exception_thread.stack.start + \ 1921 exception_thread.stack.memory.data_size 1922 stack_map = {reader.ExceptionIP(): -1} 1923 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): 1924 maybe_address = reader.ReadUIntPtr(slot) 1925 if not maybe_address in stack_map: 1926 stack_map[maybe_address] = slot 1927 heap = V8Heap(reader, stack_map) 1928 1929 print "Disassembly around exception.eip:" 1930 eip_symbol = reader.FindSymbol(reader.ExceptionIP()) 1931 if eip_symbol is not None: 1932 print eip_symbol 1933 disasm_start = reader.ExceptionIP() - EIP_PROXIMITY 1934 disasm_bytes = 2 * EIP_PROXIMITY 1935 if (options.full): 1936 full_range = reader.FindRegion(reader.ExceptionIP()) 1937 if full_range is not None: 1938 disasm_start = full_range[0] 1939 disasm_bytes = full_range[1] 1940 1941 lines = reader.GetDisasmLines(disasm_start, disasm_bytes) 1942 1943 for line in lines: 1944 print FormatDisasmLine(disasm_start, heap, line) 1945 print 1946 1947 if heap is None: 1948 heap = V8Heap(reader, None) 1949 1950 if options.full: 1951 FullDump(reader, heap) 1952 1953 if options.command: 1954 InspectionShell(reader, heap).onecmd(options.command) 1955 1956 if options.shell: 1957 try: 1958 InspectionShell(reader, heap).cmdloop("type help to get help") 1959 except KeyboardInterrupt: 1960 print "Kthxbye." 1961 elif not options.command: 1962 if reader.exception is not None: 1963 frame_pointer = reader.ExceptionFP() 1964 print "Annotated stack (from exception.esp to bottom):" 1965 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): 1966 maybe_address = reader.ReadUIntPtr(slot) 1967 heap_object = heap.FindObject(maybe_address) 1968 maybe_symbol = reader.FindSymbol(maybe_address) 1969 if slot == frame_pointer: 1970 maybe_symbol = "<---- frame pointer" 1971 frame_pointer = maybe_address 1972 print "%s: %s %s" % (reader.FormatIntPtr(slot), 1973 reader.FormatIntPtr(maybe_address), 1974 maybe_symbol or "") 1975 if heap_object: 1976 heap_object.Print(Printer()) 1977 print 1978 1979 reader.Dispose() 1980 1981 1982 if __name__ == "__main__": 1983 parser = optparse.OptionParser(USAGE) 1984 parser.add_option("-s", "--shell", dest="shell", action="store_true", 1985 help="start an interactive inspector shell") 1986 parser.add_option("-c", "--command", dest="command", default="", 1987 help="run an interactive inspector shell command and exit") 1988 parser.add_option("-f", "--full", dest="full", action="store_true", 1989 help="dump all information contained in the minidump") 1990 parser.add_option("--symdir", dest="symdir", default=".", 1991 help="directory containing *.pdb.sym file with symbols") 1992 parser.add_option("--objdump", 1993 default="/usr/bin/objdump", 1994 help="objdump tool to use [default: %default]") 1995 options, args = parser.parse_args() 1996 if os.path.exists(options.objdump): 1997 disasm.OBJDUMP_BIN = options.objdump 1998 OBJDUMP_BIN = options.objdump 1999 else: 2000 print "Cannot find %s, falling back to default objdump" % options.objdump 2001 if len(args) != 1: 2002 parser.print_help() 2003 sys.exit(1) 2004 AnalyzeMinidump(options, args[0]) 2005