1#!/usr/bin/env python 2# 3# Copyright 2011 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 30import ctypes 31import mmap 32import optparse 33import os 34import disasm 35import sys 36import types 37import codecs 38import re 39 40 41USAGE="""usage: %prog [OPTION]... 42 43Minidump analyzer. 44 45Shows the processor state at the point of exception including the 46stack of the active thread and the referenced objects in the V8 47heap. Code objects are disassembled and the addresses linked from the 48stack (pushed return addresses) are marked with "=>". 49 50 51Examples: 52 $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp 53""" 54 55DEBUG=False 56 57 58def DebugPrint(s): 59 if not DEBUG: return 60 print s 61 62 63class Descriptor(object): 64 """Descriptor of a structure in a memory.""" 65 66 def __init__(self, fields): 67 self.fields = fields 68 self.is_flexible = False 69 for _, type_or_func in fields: 70 if isinstance(type_or_func, types.FunctionType): 71 self.is_flexible = True 72 break 73 if not self.is_flexible: 74 self.ctype = Descriptor._GetCtype(fields) 75 self.size = ctypes.sizeof(self.ctype) 76 77 def Read(self, memory, offset): 78 if self.is_flexible: 79 fields_copy = self.fields[:] 80 last = 0 81 for name, type_or_func in fields_copy: 82 if isinstance(type_or_func, types.FunctionType): 83 partial_ctype = Descriptor._GetCtype(fields_copy[:last]) 84 partial_object = partial_ctype.from_buffer(memory, offset) 85 type = type_or_func(partial_object) 86 if type is not None: 87 fields_copy[last] = (name, type) 88 last += 1 89 else: 90 last += 1 91 complete_ctype = Descriptor._GetCtype(fields_copy[:last]) 92 else: 93 complete_ctype = self.ctype 94 return complete_ctype.from_buffer(memory, offset) 95 96 @staticmethod 97 def _GetCtype(fields): 98 class Raw(ctypes.Structure): 99 _fields_ = fields 100 _pack_ = 1 101 102 def __str__(self): 103 return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field)) 104 for field, _ in Raw._fields_) + "}" 105 return Raw 106 107 108# Set of structures and constants that describe the layout of minidump 109# files. Based on MSDN and Google Breakpad. 110 111MINIDUMP_HEADER = Descriptor([ 112 ("signature", ctypes.c_uint32), 113 ("version", ctypes.c_uint32), 114 ("stream_count", ctypes.c_uint32), 115 ("stream_directories_rva", ctypes.c_uint32), 116 ("checksum", ctypes.c_uint32), 117 ("time_date_stampt", ctypes.c_uint32), 118 ("flags", ctypes.c_uint64) 119]) 120 121MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([ 122 ("data_size", ctypes.c_uint32), 123 ("rva", ctypes.c_uint32) 124]) 125 126MINIDUMP_DIRECTORY = Descriptor([ 127 ("stream_type", ctypes.c_uint32), 128 ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype) 129]) 130 131MD_EXCEPTION_MAXIMUM_PARAMETERS = 15 132 133MINIDUMP_EXCEPTION = Descriptor([ 134 ("code", ctypes.c_uint32), 135 ("flags", ctypes.c_uint32), 136 ("record", ctypes.c_uint64), 137 ("address", ctypes.c_uint64), 138 ("parameter_count", ctypes.c_uint32), 139 ("unused_alignment", ctypes.c_uint32), 140 ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS) 141]) 142 143MINIDUMP_EXCEPTION_STREAM = Descriptor([ 144 ("thread_id", ctypes.c_uint32), 145 ("unused_alignment", ctypes.c_uint32), 146 ("exception", MINIDUMP_EXCEPTION.ctype), 147 ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype) 148]) 149 150# Stream types. 151MD_UNUSED_STREAM = 0 152MD_RESERVED_STREAM_0 = 1 153MD_RESERVED_STREAM_1 = 2 154MD_THREAD_LIST_STREAM = 3 155MD_MODULE_LIST_STREAM = 4 156MD_MEMORY_LIST_STREAM = 5 157MD_EXCEPTION_STREAM = 6 158MD_SYSTEM_INFO_STREAM = 7 159MD_THREAD_EX_LIST_STREAM = 8 160MD_MEMORY_64_LIST_STREAM = 9 161MD_COMMENT_STREAM_A = 10 162MD_COMMENT_STREAM_W = 11 163MD_HANDLE_DATA_STREAM = 12 164MD_FUNCTION_TABLE_STREAM = 13 165MD_UNLOADED_MODULE_LIST_STREAM = 14 166MD_MISC_INFO_STREAM = 15 167MD_MEMORY_INFO_LIST_STREAM = 16 168MD_THREAD_INFO_LIST_STREAM = 17 169MD_HANDLE_OPERATION_LIST_STREAM = 18 170 171MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80 172 173MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([ 174 ("control_word", ctypes.c_uint32), 175 ("status_word", ctypes.c_uint32), 176 ("tag_word", ctypes.c_uint32), 177 ("error_offset", ctypes.c_uint32), 178 ("error_selector", ctypes.c_uint32), 179 ("data_offset", ctypes.c_uint32), 180 ("data_selector", ctypes.c_uint32), 181 ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE), 182 ("cr0_npx_state", ctypes.c_uint32) 183]) 184 185MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512 186 187# Context flags. 188MD_CONTEXT_X86 = 0x00010000 189MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001) 190MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002) 191MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004) 192MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008) 193MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010) 194MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020) 195 196def EnableOnFlag(type, flag): 197 return lambda o: [None, type][int((o.context_flags & flag) != 0)] 198 199MINIDUMP_CONTEXT_X86 = Descriptor([ 200 ("context_flags", ctypes.c_uint32), 201 # MD_CONTEXT_X86_DEBUG_REGISTERS. 202 ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 203 ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 204 ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 205 ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 206 ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 207 ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 208 # MD_CONTEXT_X86_FLOATING_POINT. 209 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype, 210 MD_CONTEXT_X86_FLOATING_POINT)), 211 # MD_CONTEXT_X86_SEGMENTS. 212 ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)), 213 ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)), 214 ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)), 215 ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)), 216 # MD_CONTEXT_X86_INTEGER. 217 ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 218 ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 219 ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 220 ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 221 ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 222 ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 223 # MD_CONTEXT_X86_CONTROL. 224 ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 225 ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 226 ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 227 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 228 ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 229 ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 230 # MD_CONTEXT_X86_EXTENDED_REGISTERS. 231 ("extended_registers", 232 EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE, 233 MD_CONTEXT_X86_EXTENDED_REGISTERS)) 234]) 235 236MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([ 237 ("start", ctypes.c_uint64), 238 ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype) 239]) 240 241MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([ 242 ("start", ctypes.c_uint64), 243 ("size", ctypes.c_uint64) 244]) 245 246MINIDUMP_MEMORY_LIST = Descriptor([ 247 ("range_count", ctypes.c_uint32), 248 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count) 249]) 250 251MINIDUMP_MEMORY_LIST64 = Descriptor([ 252 ("range_count", ctypes.c_uint64), 253 ("base_rva", ctypes.c_uint64), 254 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count) 255]) 256 257MINIDUMP_THREAD = Descriptor([ 258 ("id", ctypes.c_uint32), 259 ("suspend_count", ctypes.c_uint32), 260 ("priority_class", ctypes.c_uint32), 261 ("priority", ctypes.c_uint32), 262 ("ted", ctypes.c_uint64), 263 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype), 264 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype) 265]) 266 267MINIDUMP_THREAD_LIST = Descriptor([ 268 ("thread_count", ctypes.c_uint32), 269 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count) 270]) 271 272 273class MinidumpReader(object): 274 """Minidump (.dmp) reader.""" 275 276 _HEADER_MAGIC = 0x504d444d 277 278 def __init__(self, options, minidump_name): 279 self.minidump_name = minidump_name 280 self.minidump_file = open(minidump_name, "r") 281 self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE) 282 self.header = MINIDUMP_HEADER.Read(self.minidump, 0) 283 if self.header.signature != MinidumpReader._HEADER_MAGIC: 284 print >>sys.stderr, "Warning: unsupported minidump header magic" 285 DebugPrint(self.header) 286 directories = [] 287 offset = self.header.stream_directories_rva 288 for _ in xrange(self.header.stream_count): 289 directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset)) 290 offset += MINIDUMP_DIRECTORY.size 291 self.exception = None 292 self.exception_context = None 293 self.memory_list = None 294 self.thread_map = {} 295 for d in directories: 296 DebugPrint(d) 297 # TODO(vitalyr): extract system info including CPU features. 298 if d.stream_type == MD_EXCEPTION_STREAM: 299 self.exception = MINIDUMP_EXCEPTION_STREAM.Read( 300 self.minidump, d.location.rva) 301 DebugPrint(self.exception) 302 self.exception_context = MINIDUMP_CONTEXT_X86.Read( 303 self.minidump, self.exception.thread_context.rva) 304 DebugPrint(self.exception_context) 305 elif d.stream_type == MD_THREAD_LIST_STREAM: 306 thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva) 307 assert ctypes.sizeof(thread_list) == d.location.data_size 308 DebugPrint(thread_list) 309 for thread in thread_list.threads: 310 DebugPrint(thread) 311 self.thread_map[thread.id] = thread 312 elif d.stream_type == MD_MEMORY_LIST_STREAM: 313 print >>sys.stderr, "Warning: not a full minidump" 314 ml = MINIDUMP_MEMORY_LIST.Read(self.minidump, d.location.rva) 315 DebugPrint(ml) 316 for m in ml.ranges: 317 DebugPrint(m) 318 elif d.stream_type == MD_MEMORY_64_LIST_STREAM: 319 assert self.memory_list is None 320 self.memory_list = MINIDUMP_MEMORY_LIST64.Read( 321 self.minidump, d.location.rva) 322 assert ctypes.sizeof(self.memory_list) == d.location.data_size 323 DebugPrint(self.memory_list) 324 325 def IsValidAddress(self, address): 326 return self.FindLocation(address) is not None 327 328 def ReadU8(self, address): 329 location = self.FindLocation(address) 330 return ctypes.c_uint8.from_buffer(self.minidump, location).value 331 332 def ReadU32(self, address): 333 location = self.FindLocation(address) 334 return ctypes.c_uint32.from_buffer(self.minidump, location).value 335 336 def ReadBytes(self, address, size): 337 location = self.FindLocation(address) 338 return self.minidump[location:location + size] 339 340 def FindLocation(self, address): 341 # TODO(vitalyr): only works for full minidumps (...64 structure variants). 342 offset = 0 343 for r in self.memory_list.ranges: 344 if r.start <= address < r.start + r.size: 345 return self.memory_list.base_rva + offset + address - r.start 346 offset += r.size 347 return None 348 349 def GetDisasmLines(self, address, size): 350 location = self.FindLocation(address) 351 if location is None: return [] 352 return disasm.GetDisasmLines(self.minidump_name, 353 location, 354 size, 355 "ia32", 356 False) 357 358 359 def Dispose(self): 360 self.minidump.close() 361 self.minidump_file.close() 362 363 364# List of V8 instance types. Obtained by adding the code below to any .cc file. 365# 366# #define DUMP_TYPE(T) printf("%d: \"%s\",\n", T, #T); 367# struct P { 368# P() { 369# printf("{\n"); 370# INSTANCE_TYPE_LIST(DUMP_TYPE) 371# printf("}\n"); 372# } 373# }; 374# static P p; 375INSTANCE_TYPES = { 376 64: "SYMBOL_TYPE", 377 68: "ASCII_SYMBOL_TYPE", 378 65: "CONS_SYMBOL_TYPE", 379 69: "CONS_ASCII_SYMBOL_TYPE", 380 66: "EXTERNAL_SYMBOL_TYPE", 381 74: "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE", 382 70: "EXTERNAL_ASCII_SYMBOL_TYPE", 383 0: "STRING_TYPE", 384 4: "ASCII_STRING_TYPE", 385 1: "CONS_STRING_TYPE", 386 5: "CONS_ASCII_STRING_TYPE", 387 2: "EXTERNAL_STRING_TYPE", 388 10: "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE", 389 6: "EXTERNAL_ASCII_STRING_TYPE", 390 6: "PRIVATE_EXTERNAL_ASCII_STRING_TYPE", 391 128: "MAP_TYPE", 392 129: "CODE_TYPE", 393 130: "ODDBALL_TYPE", 394 131: "JS_GLOBAL_PROPERTY_CELL_TYPE", 395 132: "HEAP_NUMBER_TYPE", 396 133: "PROXY_TYPE", 397 134: "BYTE_ARRAY_TYPE", 398 135: "PIXEL_ARRAY_TYPE", 399 136: "EXTERNAL_BYTE_ARRAY_TYPE", 400 137: "EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE", 401 138: "EXTERNAL_SHORT_ARRAY_TYPE", 402 139: "EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE", 403 140: "EXTERNAL_INT_ARRAY_TYPE", 404 141: "EXTERNAL_UNSIGNED_INT_ARRAY_TYPE", 405 142: "EXTERNAL_FLOAT_ARRAY_TYPE", 406 143: "FILLER_TYPE", 407 144: "ACCESSOR_INFO_TYPE", 408 145: "ACCESS_CHECK_INFO_TYPE", 409 146: "INTERCEPTOR_INFO_TYPE", 410 147: "CALL_HANDLER_INFO_TYPE", 411 148: "FUNCTION_TEMPLATE_INFO_TYPE", 412 149: "OBJECT_TEMPLATE_INFO_TYPE", 413 150: "SIGNATURE_INFO_TYPE", 414 151: "TYPE_SWITCH_INFO_TYPE", 415 152: "SCRIPT_TYPE", 416 153: "CODE_CACHE_TYPE", 417 156: "FIXED_ARRAY_TYPE", 418 157: "SHARED_FUNCTION_INFO_TYPE", 419 158: "JS_MESSAGE_OBJECT_TYPE", 420 159: "JS_VALUE_TYPE", 421 160: "JS_OBJECT_TYPE", 422 161: "JS_CONTEXT_EXTENSION_OBJECT_TYPE", 423 162: "JS_GLOBAL_OBJECT_TYPE", 424 163: "JS_BUILTINS_OBJECT_TYPE", 425 164: "JS_GLOBAL_PROXY_TYPE", 426 165: "JS_ARRAY_TYPE", 427 166: "JS_REGEXP_TYPE", 428 167: "JS_FUNCTION_TYPE", 429 154: "DEBUG_INFO_TYPE", 430 155: "BREAK_POINT_INFO_TYPE", 431} 432 433 434class Printer(object): 435 """Printer with indentation support.""" 436 437 def __init__(self): 438 self.indent = 0 439 440 def Indent(self): 441 self.indent += 2 442 443 def Dedent(self): 444 self.indent -= 2 445 446 def Print(self, string): 447 print "%s%s" % (self._IndentString(), string) 448 449 def PrintLines(self, lines): 450 indent = self._IndentString() 451 print "\n".join("%s%s" % (indent, line) for line in lines) 452 453 def _IndentString(self): 454 return self.indent * " " 455 456 457ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+") 458 459 460def FormatDisasmLine(start, heap, line): 461 line_address = start + line[0] 462 stack_slot = heap.stack_map.get(line_address) 463 marker = " " 464 if stack_slot: 465 marker = "=>" 466 code = AnnotateAddresses(heap, line[1]) 467 return "%s%08x %08x: %s" % (marker, line_address, line[0], code) 468 469 470def AnnotateAddresses(heap, line): 471 extra = [] 472 for m in ADDRESS_RE.finditer(line): 473 maybe_address = int(m.group(0), 16) 474 object = heap.FindObject(maybe_address) 475 if not object: continue 476 extra.append(str(object)) 477 if len(extra) == 0: return line 478 return "%s ;; %s" % (line, ", ".join(extra)) 479 480 481class HeapObject(object): 482 def __init__(self, heap, map, address): 483 self.heap = heap 484 self.map = map 485 self.address = address 486 487 def Is(self, cls): 488 return isinstance(self, cls) 489 490 def Print(self, p): 491 p.Print(str(self)) 492 493 def __str__(self): 494 return "HeapObject(%08x, %s)" % (self.address, 495 INSTANCE_TYPES[self.map.instance_type]) 496 497 def ObjectField(self, offset): 498 field_value = self.heap.reader.ReadU32(self.address + offset) 499 return self.heap.FindObjectOrSmi(field_value) 500 501 def SmiField(self, offset): 502 field_value = self.heap.reader.ReadU32(self.address + offset) 503 assert (field_value & 1) == 0 504 return field_value / 2 505 506 507class Map(HeapObject): 508 INSTANCE_TYPE_OFFSET = 8 509 510 def __init__(self, heap, map, address): 511 HeapObject.__init__(self, heap, map, address) 512 self.instance_type = \ 513 heap.reader.ReadU8(self.address + Map.INSTANCE_TYPE_OFFSET) 514 515 516class String(HeapObject): 517 LENGTH_OFFSET = 4 518 519 def __init__(self, heap, map, address): 520 HeapObject.__init__(self, heap, map, address) 521 self.length = self.SmiField(String.LENGTH_OFFSET) 522 523 def GetChars(self): 524 return "?string?" 525 526 def Print(self, p): 527 p.Print(str(self)) 528 529 def __str__(self): 530 return "\"%s\"" % self.GetChars() 531 532 533class SeqString(String): 534 CHARS_OFFSET = 12 535 536 def __init__(self, heap, map, address): 537 String.__init__(self, heap, map, address) 538 self.chars = heap.reader.ReadBytes(self.address + SeqString.CHARS_OFFSET, 539 self.length) 540 541 def GetChars(self): 542 return self.chars 543 544 545class ExternalString(String): 546 RESOURCE_OFFSET = 12 547 548 WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4 549 WEBKIT_STRING_IMPL_CHARS_OFFSET = 8 550 551 def __init__(self, heap, map, address): 552 String.__init__(self, heap, map, address) 553 reader = heap.reader 554 self.resource = \ 555 reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET) 556 self.chars = "?external string?" 557 if not reader.IsValidAddress(self.resource): return 558 string_impl_address = self.resource + \ 559 ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET 560 if not reader.IsValidAddress(string_impl_address): return 561 string_impl = reader.ReadU32(string_impl_address) 562 chars_ptr_address = string_impl + \ 563 ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET 564 if not reader.IsValidAddress(chars_ptr_address): return 565 chars_ptr = reader.ReadU32(chars_ptr_address) 566 if not reader.IsValidAddress(chars_ptr): return 567 raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length) 568 self.chars = codecs.getdecoder("utf16")(raw_chars)[0] 569 570 def GetChars(self): 571 return self.chars 572 573 574class ConsString(String): 575 LEFT_OFFSET = 12 576 RIGHT_OFFSET = 16 577 578 def __init__(self, heap, map, address): 579 String.__init__(self, heap, map, address) 580 self.left = self.ObjectField(ConsString.LEFT_OFFSET) 581 self.right = self.ObjectField(ConsString.RIGHT_OFFSET) 582 583 def GetChars(self): 584 return self.left.GetChars() + self.right.GetChars() 585 586 587class Oddball(HeapObject): 588 TO_STRING_OFFSET = 4 589 590 def __init__(self, heap, map, address): 591 HeapObject.__init__(self, heap, map, address) 592 self.to_string = self.ObjectField(Oddball.TO_STRING_OFFSET) 593 594 def Print(self, p): 595 p.Print(str(self)) 596 597 def __str__(self): 598 return "<%s>" % self.to_string.GetChars() 599 600 601class FixedArray(HeapObject): 602 LENGTH_OFFSET = 4 603 ELEMENTS_OFFSET = 8 604 605 def __init__(self, heap, map, address): 606 HeapObject.__init__(self, heap, map, address) 607 self.length = self.SmiField(FixedArray.LENGTH_OFFSET) 608 609 def Print(self, p): 610 p.Print("FixedArray(%08x) {" % self.address) 611 p.Indent() 612 p.Print("length: %d" % self.length) 613 for i in xrange(self.length): 614 offset = FixedArray.ELEMENTS_OFFSET + 4 * i 615 p.Print("[%08d] = %s" % (i, self.ObjectField(offset))) 616 p.Dedent() 617 p.Print("}") 618 619 def __str__(self): 620 return "FixedArray(%08x, length=%d)" % (self.address, self.length) 621 622 623class JSFunction(HeapObject): 624 CODE_ENTRY_OFFSET = 12 625 SHARED_OFFSET = 20 626 627 def __init__(self, heap, map, address): 628 HeapObject.__init__(self, heap, map, address) 629 code_entry = \ 630 heap.reader.ReadU32(self.address + JSFunction.CODE_ENTRY_OFFSET) 631 self.code = heap.FindObject(code_entry - Code.ENTRY_OFFSET + 1) 632 self.shared = self.ObjectField(JSFunction.SHARED_OFFSET) 633 634 def Print(self, p): 635 source = "\n".join(" %s" % line for line in self._GetSource().split("\n")) 636 p.Print("JSFunction(%08x) {" % self.address) 637 p.Indent() 638 p.Print("inferred name: %s" % self.shared.inferred_name) 639 if self.shared.script.Is(Script) and self.shared.script.name.Is(String): 640 p.Print("script name: %s" % self.shared.script.name) 641 p.Print("source:") 642 p.PrintLines(self._GetSource().split("\n")) 643 p.Print("code:") 644 self.code.Print(p) 645 if self.code != self.shared.code: 646 p.Print("unoptimized code:") 647 self.shared.code.Print(p) 648 p.Dedent() 649 p.Print("}") 650 651 def __str__(self): 652 inferred_name = "" 653 if self.shared.Is(SharedFunctionInfo): 654 inferred_name = self.shared.inferred_name 655 return "JSFunction(%08x, %s)" % (self.address, inferred_name) 656 657 def _GetSource(self): 658 source = "?source?" 659 start = self.shared.start_position 660 end = self.shared.end_position 661 if not self.shared.script.Is(Script): return source 662 script_source = self.shared.script.source 663 if not script_source.Is(String): return source 664 return script_source.GetChars()[start:end] 665 666 667class SharedFunctionInfo(HeapObject): 668 CODE_OFFSET = 2 * 4 669 SCRIPT_OFFSET = 7 * 4 670 INFERRED_NAME_OFFSET = 9 * 4 671 START_POSITION_AND_TYPE_OFFSET = 17 * 4 672 END_POSITION_OFFSET = 18 * 4 673 674 def __init__(self, heap, map, address): 675 HeapObject.__init__(self, heap, map, address) 676 self.code = self.ObjectField(SharedFunctionInfo.CODE_OFFSET) 677 self.script = self.ObjectField(SharedFunctionInfo.SCRIPT_OFFSET) 678 self.inferred_name = \ 679 self.ObjectField(SharedFunctionInfo.INFERRED_NAME_OFFSET) 680 start_position_and_type = \ 681 self.SmiField(SharedFunctionInfo.START_POSITION_AND_TYPE_OFFSET) 682 self.start_position = start_position_and_type >> 2 683 self.end_position = self.SmiField(SharedFunctionInfo.END_POSITION_OFFSET) 684 685 686class Script(HeapObject): 687 SOURCE_OFFSET = 4 688 NAME_OFFSET = 8 689 690 def __init__(self, heap, map, address): 691 HeapObject.__init__(self, heap, map, address) 692 self.source = self.ObjectField(Script.SOURCE_OFFSET) 693 self.name = self.ObjectField(Script.NAME_OFFSET) 694 695 696class Code(HeapObject): 697 INSTRUCTION_SIZE_OFFSET = 4 698 ENTRY_OFFSET = 32 699 700 def __init__(self, heap, map, address): 701 HeapObject.__init__(self, heap, map, address) 702 self.entry = self.address + Code.ENTRY_OFFSET 703 self.instruction_size = \ 704 heap.reader.ReadU32(self.address + Code.INSTRUCTION_SIZE_OFFSET) 705 706 def Print(self, p): 707 lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size) 708 p.Print("Code(%08x) {" % self.address) 709 p.Indent() 710 p.Print("instruction_size: %d" % self.instruction_size) 711 p.PrintLines(self._FormatLine(line) for line in lines) 712 p.Dedent() 713 p.Print("}") 714 715 def _FormatLine(self, line): 716 return FormatDisasmLine(self.entry, self.heap, line) 717 718 719class V8Heap(object): 720 CLASS_MAP = { 721 "SYMBOL_TYPE": SeqString, 722 "ASCII_SYMBOL_TYPE": SeqString, 723 "CONS_SYMBOL_TYPE": ConsString, 724 "CONS_ASCII_SYMBOL_TYPE": ConsString, 725 "EXTERNAL_SYMBOL_TYPE": ExternalString, 726 "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString, 727 "EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString, 728 "STRING_TYPE": SeqString, 729 "ASCII_STRING_TYPE": SeqString, 730 "CONS_STRING_TYPE": ConsString, 731 "CONS_ASCII_STRING_TYPE": ConsString, 732 "EXTERNAL_STRING_TYPE": ExternalString, 733 "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE": ExternalString, 734 "EXTERNAL_ASCII_STRING_TYPE": ExternalString, 735 736 "MAP_TYPE": Map, 737 "ODDBALL_TYPE": Oddball, 738 "FIXED_ARRAY_TYPE": FixedArray, 739 "JS_FUNCTION_TYPE": JSFunction, 740 "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo, 741 "SCRIPT_TYPE": Script, 742 "CODE_TYPE": Code 743 } 744 745 def __init__(self, reader, stack_map): 746 self.reader = reader 747 self.stack_map = stack_map 748 self.objects = {} 749 750 def FindObjectOrSmi(self, tagged_address): 751 if (tagged_address & 1) == 0: return tagged_address / 2 752 return self.FindObject(tagged_address) 753 754 def FindObject(self, tagged_address): 755 if tagged_address in self.objects: 756 return self.objects[tagged_address] 757 if (tagged_address & 1) != 1: return None 758 address = tagged_address - 1 759 if not self.reader.IsValidAddress(address): return None 760 map_tagged_address = self.reader.ReadU32(address) 761 if tagged_address == map_tagged_address: 762 # Meta map? 763 meta_map = Map(self, None, address) 764 instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type) 765 if instance_type_name != "MAP_TYPE": return None 766 meta_map.map = meta_map 767 object = meta_map 768 else: 769 map = self.FindObject(map_tagged_address) 770 if map is None: return None 771 instance_type_name = INSTANCE_TYPES.get(map.instance_type) 772 if instance_type_name is None: return None 773 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject) 774 object = cls(self, map, address) 775 self.objects[tagged_address] = object 776 return object 777 778 779EIP_PROXIMITY = 64 780 781 782def AnalyzeMinidump(options, minidump_name): 783 reader = MinidumpReader(options, minidump_name) 784 DebugPrint("========================================") 785 if reader.exception is None: 786 print "Minidump has no exception info" 787 return 788 print "Exception info:" 789 exception_thread = reader.thread_map[reader.exception.thread_id] 790 print " thread id: %d" % exception_thread.id 791 print " code: %08X" % reader.exception.exception.code 792 print " context:" 793 print " eax: %08x" % reader.exception_context.eax 794 print " ebx: %08x" % reader.exception_context.ebx 795 print " ecx: %08x" % reader.exception_context.ecx 796 print " edx: %08x" % reader.exception_context.edx 797 print " edi: %08x" % reader.exception_context.edi 798 print " esi: %08x" % reader.exception_context.esi 799 print " ebp: %08x" % reader.exception_context.ebp 800 print " esp: %08x" % reader.exception_context.esp 801 print " eip: %08x" % reader.exception_context.eip 802 # TODO(vitalyr): decode eflags. 803 print " eflags: %s" % bin(reader.exception_context.eflags)[2:] 804 print 805 806 stack_bottom = exception_thread.stack.start + \ 807 exception_thread.stack.memory.data_size 808 stack_map = {reader.exception_context.eip: -1} 809 for slot in xrange(reader.exception_context.esp, stack_bottom, 4): 810 maybe_address = reader.ReadU32(slot) 811 if not maybe_address in stack_map: 812 stack_map[maybe_address] = slot 813 heap = V8Heap(reader, stack_map) 814 815 print "Disassembly around exception.eip:" 816 start = reader.exception_context.eip - EIP_PROXIMITY 817 lines = reader.GetDisasmLines(start, 2 * EIP_PROXIMITY) 818 for line in lines: 819 print FormatDisasmLine(start, heap, line) 820 print 821 822 print "Annotated stack (from exception.esp to bottom):" 823 for slot in xrange(reader.exception_context.esp, stack_bottom, 4): 824 maybe_address = reader.ReadU32(slot) 825 heap_object = heap.FindObject(maybe_address) 826 print "%08x: %08x" % (slot, maybe_address) 827 if heap_object: 828 heap_object.Print(Printer()) 829 print 830 831 reader.Dispose() 832 833 834if __name__ == "__main__": 835 parser = optparse.OptionParser(USAGE) 836 options, args = parser.parse_args() 837 if len(args) != 1: 838 parser.print_help() 839 sys.exit(1) 840 AnalyzeMinidump(options, args[0]) 841