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 30import BaseHTTPServer 31import bisect 32import cgi 33import cmd 34import codecs 35import ctypes 36import datetime 37import disasm 38import inspect 39import mmap 40import optparse 41import os 42import re 43import StringIO 44import sys 45import types 46import urllib 47import urlparse 48import v8heapconst 49import webbrowser 50 51PORT_NUMBER = 8081 52 53 54USAGE="""usage: %prog [OPTIONS] [DUMP-FILE] 55 56Minidump analyzer. 57 58Shows the processor state at the point of exception including the 59stack of the active thread and the referenced objects in the V8 60heap. Code objects are disassembled and the addresses linked from the 61stack (e.g. pushed return addresses) are marked with "=>". 62 63Examples: 64 $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp""" 65 66 67DEBUG=False 68 69 70def DebugPrint(s): 71 if not DEBUG: return 72 print s 73 74 75class Descriptor(object): 76 """Descriptor of a structure in a memory.""" 77 78 def __init__(self, fields): 79 self.fields = fields 80 self.is_flexible = False 81 for _, type_or_func in fields: 82 if isinstance(type_or_func, types.FunctionType): 83 self.is_flexible = True 84 break 85 if not self.is_flexible: 86 self.ctype = Descriptor._GetCtype(fields) 87 self.size = ctypes.sizeof(self.ctype) 88 89 def Read(self, memory, offset): 90 if self.is_flexible: 91 fields_copy = self.fields[:] 92 last = 0 93 for name, type_or_func in fields_copy: 94 if isinstance(type_or_func, types.FunctionType): 95 partial_ctype = Descriptor._GetCtype(fields_copy[:last]) 96 partial_object = partial_ctype.from_buffer(memory, offset) 97 type = type_or_func(partial_object) 98 if type is not None: 99 fields_copy[last] = (name, type) 100 last += 1 101 else: 102 last += 1 103 complete_ctype = Descriptor._GetCtype(fields_copy[:last]) 104 else: 105 complete_ctype = self.ctype 106 return complete_ctype.from_buffer(memory, offset) 107 108 @staticmethod 109 def _GetCtype(fields): 110 class Raw(ctypes.Structure): 111 _fields_ = fields 112 _pack_ = 1 113 114 def __str__(self): 115 return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field)) 116 for field, _ in Raw._fields_) + "}" 117 return Raw 118 119 120def FullDump(reader, heap): 121 """Dump all available memory regions.""" 122 def dump_region(reader, start, size, location): 123 print 124 while start & 3 != 0: 125 start += 1 126 size -= 1 127 location += 1 128 is_executable = reader.IsProbableExecutableRegion(location, size) 129 is_ascii = reader.IsProbableASCIIRegion(location, size) 130 131 if is_executable is not False: 132 lines = reader.GetDisasmLines(start, size) 133 for line in lines: 134 print FormatDisasmLine(start, heap, line) 135 print 136 137 if is_ascii is not False: 138 # Output in the same format as the Unix hd command 139 addr = start 140 for i in xrange(0, size, 16): 141 slot = i + location 142 hex_line = "" 143 asc_line = "" 144 for i in xrange(16): 145 if slot + i < location + size: 146 byte = ctypes.c_uint8.from_buffer(reader.minidump, slot + i).value 147 if byte >= 0x20 and byte < 0x7f: 148 asc_line += chr(byte) 149 else: 150 asc_line += "." 151 hex_line += " %02x" % (byte) 152 else: 153 hex_line += " " 154 if i == 7: 155 hex_line += " " 156 print "%s %s |%s|" % (reader.FormatIntPtr(addr), 157 hex_line, 158 asc_line) 159 addr += 16 160 161 if is_executable is not True and is_ascii is not True: 162 print "%s - %s" % (reader.FormatIntPtr(start), 163 reader.FormatIntPtr(start + size)) 164 print start + size + 1; 165 for i in xrange(0, size, reader.PointerSize()): 166 slot = start + i 167 maybe_address = reader.ReadUIntPtr(slot) 168 heap_object = heap.FindObject(maybe_address) 169 print "%s: %s" % (reader.FormatIntPtr(slot), 170 reader.FormatIntPtr(maybe_address)) 171 if heap_object: 172 heap_object.Print(Printer()) 173 print 174 175 reader.ForEachMemoryRegion(dump_region) 176 177# Heap constants generated by 'make grokdump' in v8heapconst module. 178INSTANCE_TYPES = v8heapconst.INSTANCE_TYPES 179KNOWN_MAPS = v8heapconst.KNOWN_MAPS 180KNOWN_OBJECTS = v8heapconst.KNOWN_OBJECTS 181FRAME_MARKERS = v8heapconst.FRAME_MARKERS 182 183# Markers pushed on the stack by PushStackTraceAndDie 184MAGIC_MARKER_PAIRS = ( 185 (0xbbbbbbbb, 0xbbbbbbbb), 186 (0xfefefefe, 0xfefefeff), 187) 188# See StackTraceFailureMessage in isolate.h 189STACK_TRACE_MARKER = 0xdecade30 190# See FailureMessage in logging.cc 191ERROR_MESSAGE_MARKER = 0xdecade10 192 193# Set of structures and constants that describe the layout of minidump 194# files. Based on MSDN and Google Breakpad. 195 196MINIDUMP_HEADER = Descriptor([ 197 ("signature", ctypes.c_uint32), 198 ("version", ctypes.c_uint32), 199 ("stream_count", ctypes.c_uint32), 200 ("stream_directories_rva", ctypes.c_uint32), 201 ("checksum", ctypes.c_uint32), 202 ("time_date_stampt", ctypes.c_uint32), 203 ("flags", ctypes.c_uint64) 204]) 205 206MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([ 207 ("data_size", ctypes.c_uint32), 208 ("rva", ctypes.c_uint32) 209]) 210 211MINIDUMP_STRING = Descriptor([ 212 ("length", ctypes.c_uint32), 213 ("buffer", lambda t: ctypes.c_uint8 * (t.length + 2)) 214]) 215 216MINIDUMP_DIRECTORY = Descriptor([ 217 ("stream_type", ctypes.c_uint32), 218 ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype) 219]) 220 221MD_EXCEPTION_MAXIMUM_PARAMETERS = 15 222 223MINIDUMP_EXCEPTION = Descriptor([ 224 ("code", ctypes.c_uint32), 225 ("flags", ctypes.c_uint32), 226 ("record", ctypes.c_uint64), 227 ("address", ctypes.c_uint64), 228 ("parameter_count", ctypes.c_uint32), 229 ("unused_alignment", ctypes.c_uint32), 230 ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS) 231]) 232 233MINIDUMP_EXCEPTION_STREAM = Descriptor([ 234 ("thread_id", ctypes.c_uint32), 235 ("unused_alignment", ctypes.c_uint32), 236 ("exception", MINIDUMP_EXCEPTION.ctype), 237 ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype) 238]) 239 240# Stream types. 241MD_UNUSED_STREAM = 0 242MD_RESERVED_STREAM_0 = 1 243MD_RESERVED_STREAM_1 = 2 244MD_THREAD_LIST_STREAM = 3 245MD_MODULE_LIST_STREAM = 4 246MD_MEMORY_LIST_STREAM = 5 247MD_EXCEPTION_STREAM = 6 248MD_SYSTEM_INFO_STREAM = 7 249MD_THREAD_EX_LIST_STREAM = 8 250MD_MEMORY_64_LIST_STREAM = 9 251MD_COMMENT_STREAM_A = 10 252MD_COMMENT_STREAM_W = 11 253MD_HANDLE_DATA_STREAM = 12 254MD_FUNCTION_TABLE_STREAM = 13 255MD_UNLOADED_MODULE_LIST_STREAM = 14 256MD_MISC_INFO_STREAM = 15 257MD_MEMORY_INFO_LIST_STREAM = 16 258MD_THREAD_INFO_LIST_STREAM = 17 259MD_HANDLE_OPERATION_LIST_STREAM = 18 260 261MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80 262 263MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([ 264 ("control_word", ctypes.c_uint32), 265 ("status_word", ctypes.c_uint32), 266 ("tag_word", ctypes.c_uint32), 267 ("error_offset", ctypes.c_uint32), 268 ("error_selector", ctypes.c_uint32), 269 ("data_offset", ctypes.c_uint32), 270 ("data_selector", ctypes.c_uint32), 271 ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE), 272 ("cr0_npx_state", ctypes.c_uint32) 273]) 274 275MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512 276 277# Context flags. 278MD_CONTEXT_X86 = 0x00010000 279MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001) 280MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002) 281MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004) 282MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008) 283MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010) 284MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020) 285 286def EnableOnFlag(type, flag): 287 return lambda o: [None, type][int((o.context_flags & flag) != 0)] 288 289MINIDUMP_CONTEXT_X86 = Descriptor([ 290 ("context_flags", ctypes.c_uint32), 291 # MD_CONTEXT_X86_DEBUG_REGISTERS. 292 ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 293 ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 294 ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 295 ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 296 ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 297 ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)), 298 # MD_CONTEXT_X86_FLOATING_POINT. 299 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype, 300 MD_CONTEXT_X86_FLOATING_POINT)), 301 # MD_CONTEXT_X86_SEGMENTS. 302 ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)), 303 ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)), 304 ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)), 305 ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)), 306 # MD_CONTEXT_X86_INTEGER. 307 ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 308 ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 309 ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 310 ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 311 ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 312 ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)), 313 # MD_CONTEXT_X86_CONTROL. 314 ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 315 ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 316 ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 317 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 318 ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 319 ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 320 # MD_CONTEXT_X86_EXTENDED_REGISTERS. 321 ("extended_registers", 322 EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE, 323 MD_CONTEXT_X86_EXTENDED_REGISTERS)) 324]) 325 326MD_CONTEXT_ARM = 0x40000000 327MD_CONTEXT_ARM_INTEGER = (MD_CONTEXT_ARM | 0x00000002) 328MD_CONTEXT_ARM_FLOATING_POINT = (MD_CONTEXT_ARM | 0x00000004) 329MD_FLOATINGSAVEAREA_ARM_FPR_COUNT = 32 330MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT = 8 331 332MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([ 333 ("fpscr", ctypes.c_uint64), 334 ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPR_COUNT), 335 ("extra", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT) 336]) 337 338MINIDUMP_CONTEXT_ARM = Descriptor([ 339 ("context_flags", ctypes.c_uint32), 340 # MD_CONTEXT_ARM_INTEGER. 341 ("r0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 342 ("r1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 343 ("r2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 344 ("r3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 345 ("r4", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 346 ("r5", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 347 ("r6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 348 ("r7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 349 ("r8", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 350 ("r9", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 351 ("r10", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 352 ("r11", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 353 ("r12", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 354 ("sp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 355 ("lr", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 356 ("pc", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)), 357 ("cpsr", ctypes.c_uint32), 358 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype, 359 MD_CONTEXT_ARM_FLOATING_POINT)) 360]) 361 362 363MD_CONTEXT_ARM64 = 0x80000000 364MD_CONTEXT_ARM64_INTEGER = (MD_CONTEXT_ARM64 | 0x00000002) 365MD_CONTEXT_ARM64_FLOATING_POINT = (MD_CONTEXT_ARM64 | 0x00000004) 366MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT = 64 367 368MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([ 369 ("fpscr", ctypes.c_uint64), 370 ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT), 371]) 372 373MINIDUMP_CONTEXT_ARM64 = Descriptor([ 374 ("context_flags", ctypes.c_uint64), 375 # MD_CONTEXT_ARM64_INTEGER. 376 ("r0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 377 ("r1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 378 ("r2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 379 ("r3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 380 ("r4", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 381 ("r5", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 382 ("r6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 383 ("r7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 384 ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 385 ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 386 ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 387 ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 388 ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 389 ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 390 ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 391 ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 392 ("r16", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 393 ("r17", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 394 ("r18", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 395 ("r19", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 396 ("r20", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 397 ("r21", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 398 ("r22", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 399 ("r23", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 400 ("r24", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 401 ("r25", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 402 ("r26", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 403 ("r27", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 404 ("r28", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 405 ("fp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 406 ("lr", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 407 ("sp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 408 ("pc", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)), 409 ("cpsr", ctypes.c_uint32), 410 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype, 411 MD_CONTEXT_ARM64_FLOATING_POINT)) 412]) 413 414 415MD_CONTEXT_AMD64 = 0x00100000 416MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001) 417MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002) 418MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004) 419MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008) 420MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010) 421 422MINIDUMP_CONTEXT_AMD64 = Descriptor([ 423 ("p1_home", ctypes.c_uint64), 424 ("p2_home", ctypes.c_uint64), 425 ("p3_home", ctypes.c_uint64), 426 ("p4_home", ctypes.c_uint64), 427 ("p5_home", ctypes.c_uint64), 428 ("p6_home", ctypes.c_uint64), 429 ("context_flags", ctypes.c_uint32), 430 ("mx_csr", ctypes.c_uint32), 431 # MD_CONTEXT_AMD64_CONTROL. 432 ("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)), 433 # MD_CONTEXT_AMD64_SEGMENTS 434 ("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)), 435 ("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)), 436 ("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)), 437 ("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)), 438 # MD_CONTEXT_AMD64_CONTROL. 439 ("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)), 440 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)), 441 # MD_CONTEXT_AMD64_DEBUG_REGISTERS. 442 ("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 443 ("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 444 ("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 445 ("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 446 ("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 447 ("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 448 # MD_CONTEXT_AMD64_INTEGER. 449 ("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 450 ("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 451 ("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 452 ("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 453 # MD_CONTEXT_AMD64_CONTROL. 454 ("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)), 455 # MD_CONTEXT_AMD64_INTEGER. 456 ("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 457 ("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 458 ("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 459 ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 460 ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 461 ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 462 ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 463 ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 464 ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 465 ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 466 ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), 467 # MD_CONTEXT_AMD64_CONTROL. 468 ("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)), 469 # MD_CONTEXT_AMD64_FLOATING_POINT 470 ("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26), 471 MD_CONTEXT_AMD64_FLOATING_POINT)), 472 ("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26), 473 MD_CONTEXT_AMD64_FLOATING_POINT)), 474 ("vector_control", EnableOnFlag(ctypes.c_uint64, 475 MD_CONTEXT_AMD64_FLOATING_POINT)), 476 # MD_CONTEXT_AMD64_DEBUG_REGISTERS. 477 ("debug_control", EnableOnFlag(ctypes.c_uint64, 478 MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 479 ("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64, 480 MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 481 ("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64, 482 MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 483 ("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64, 484 MD_CONTEXT_AMD64_DEBUG_REGISTERS)), 485 ("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64, 486 MD_CONTEXT_AMD64_DEBUG_REGISTERS)) 487]) 488 489MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([ 490 ("start", ctypes.c_uint64), 491 ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype) 492]) 493 494MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([ 495 ("start", ctypes.c_uint64), 496 ("size", ctypes.c_uint64) 497]) 498 499MINIDUMP_MEMORY_LIST = Descriptor([ 500 ("range_count", ctypes.c_uint32), 501 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count) 502]) 503 504MINIDUMP_MEMORY_LIST_Mac = Descriptor([ 505 ("range_count", ctypes.c_uint32), 506 ("junk", ctypes.c_uint32), 507 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count) 508]) 509 510MINIDUMP_MEMORY_LIST64 = Descriptor([ 511 ("range_count", ctypes.c_uint64), 512 ("base_rva", ctypes.c_uint64), 513 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count) 514]) 515 516MINIDUMP_THREAD = Descriptor([ 517 ("id", ctypes.c_uint32), 518 ("suspend_count", ctypes.c_uint32), 519 ("priority_class", ctypes.c_uint32), 520 ("priority", ctypes.c_uint32), 521 ("ted", ctypes.c_uint64), 522 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype), 523 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype) 524]) 525 526MINIDUMP_THREAD_LIST = Descriptor([ 527 ("thread_count", ctypes.c_uint32), 528 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count) 529]) 530 531MINIDUMP_THREAD_LIST_Mac = Descriptor([ 532 ("thread_count", ctypes.c_uint32), 533 ("junk", ctypes.c_uint32), 534 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count) 535]) 536 537MINIDUMP_VS_FIXEDFILEINFO = Descriptor([ 538 ("dwSignature", ctypes.c_uint32), 539 ("dwStrucVersion", ctypes.c_uint32), 540 ("dwFileVersionMS", ctypes.c_uint32), 541 ("dwFileVersionLS", ctypes.c_uint32), 542 ("dwProductVersionMS", ctypes.c_uint32), 543 ("dwProductVersionLS", ctypes.c_uint32), 544 ("dwFileFlagsMask", ctypes.c_uint32), 545 ("dwFileFlags", ctypes.c_uint32), 546 ("dwFileOS", ctypes.c_uint32), 547 ("dwFileType", ctypes.c_uint32), 548 ("dwFileSubtype", ctypes.c_uint32), 549 ("dwFileDateMS", ctypes.c_uint32), 550 ("dwFileDateLS", ctypes.c_uint32) 551]) 552 553MINIDUMP_RAW_MODULE = Descriptor([ 554 ("base_of_image", ctypes.c_uint64), 555 ("size_of_image", ctypes.c_uint32), 556 ("checksum", ctypes.c_uint32), 557 ("time_date_stamp", ctypes.c_uint32), 558 ("module_name_rva", ctypes.c_uint32), 559 ("version_info", MINIDUMP_VS_FIXEDFILEINFO.ctype), 560 ("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype), 561 ("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype), 562 ("reserved0", ctypes.c_uint32 * 2), 563 ("reserved1", ctypes.c_uint32 * 2) 564]) 565 566MINIDUMP_MODULE_LIST = Descriptor([ 567 ("number_of_modules", ctypes.c_uint32), 568 ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules) 569]) 570 571MINIDUMP_MODULE_LIST_Mac = Descriptor([ 572 ("number_of_modules", ctypes.c_uint32), 573 ("junk", ctypes.c_uint32), 574 ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules) 575]) 576 577MINIDUMP_RAW_SYSTEM_INFO = Descriptor([ 578 ("processor_architecture", ctypes.c_uint16) 579]) 580 581MD_CPU_ARCHITECTURE_X86 = 0 582MD_CPU_ARCHITECTURE_ARM = 5 583MD_CPU_ARCHITECTURE_ARM64 = 0x8003 584MD_CPU_ARCHITECTURE_AMD64 = 9 585 586OBJDUMP_BIN = None 587DEFAULT_OBJDUMP_BIN = '/usr/bin/objdump' 588 589class FuncSymbol: 590 def __init__(self, start, size, name): 591 self.start = start 592 self.end = self.start + size 593 self.name = name 594 595 def __cmp__(self, other): 596 if isinstance(other, FuncSymbol): 597 return self.start - other.start 598 return self.start - other 599 600 def Covers(self, addr): 601 return (self.start <= addr) and (addr < self.end) 602 603class MinidumpReader(object): 604 """Minidump (.dmp) reader.""" 605 606 _HEADER_MAGIC = 0x504d444d 607 608 def __init__(self, options, minidump_name): 609 self.minidump_name = minidump_name 610 self.minidump_file = open(minidump_name, "r") 611 self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE) 612 self.header = MINIDUMP_HEADER.Read(self.minidump, 0) 613 if self.header.signature != MinidumpReader._HEADER_MAGIC: 614 print >>sys.stderr, "Warning: Unsupported minidump header magic!" 615 DebugPrint(self.header) 616 directories = [] 617 offset = self.header.stream_directories_rva 618 for _ in xrange(self.header.stream_count): 619 directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset)) 620 offset += MINIDUMP_DIRECTORY.size 621 self.arch = None 622 self.exception = None 623 self.exception_context = None 624 self.memory_list = None 625 self.memory_list64 = None 626 self.module_list = None 627 self.thread_map = {} 628 629 self.symdir = options.symdir 630 self.modules_with_symbols = [] 631 self.symbols = [] 632 633 self._ReadArchitecture(directories) 634 self._ReadDirectories(directories) 635 self._FindObjdump(options) 636 637 def _ReadArchitecture(self, directories): 638 # Find MDRawSystemInfo stream and determine arch. 639 for d in directories: 640 if d.stream_type == MD_SYSTEM_INFO_STREAM: 641 system_info = MINIDUMP_RAW_SYSTEM_INFO.Read( 642 self.minidump, d.location.rva) 643 self.arch = system_info.processor_architecture 644 assert self.arch in [MD_CPU_ARCHITECTURE_AMD64, 645 MD_CPU_ARCHITECTURE_ARM, 646 MD_CPU_ARCHITECTURE_ARM64, 647 MD_CPU_ARCHITECTURE_X86] 648 assert not self.arch is None 649 650 def _ReadDirectories(self, directories): 651 for d in directories: 652 DebugPrint(d) 653 if d.stream_type == MD_EXCEPTION_STREAM: 654 self.exception = MINIDUMP_EXCEPTION_STREAM.Read( 655 self.minidump, d.location.rva) 656 DebugPrint(self.exception) 657 self.exception_context = self.ContextDescriptor().Read( 658 self.minidump, self.exception.thread_context.rva) 659 DebugPrint(self.exception_context) 660 elif d.stream_type == MD_THREAD_LIST_STREAM: 661 thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva) 662 if ctypes.sizeof(thread_list) + 4 == d.location.data_size: 663 thread_list = MINIDUMP_THREAD_LIST_Mac.Read( 664 self.minidump, d.location.rva) 665 assert ctypes.sizeof(thread_list) == d.location.data_size 666 DebugPrint(thread_list) 667 for thread in thread_list.threads: 668 DebugPrint(thread) 669 self.thread_map[thread.id] = thread 670 elif d.stream_type == MD_MODULE_LIST_STREAM: 671 assert self.module_list is None 672 self.module_list = MINIDUMP_MODULE_LIST.Read( 673 self.minidump, d.location.rva) 674 if ctypes.sizeof(self.module_list) + 4 == d.location.data_size: 675 self.module_list = MINIDUMP_MODULE_LIST_Mac.Read( 676 self.minidump, d.location.rva) 677 assert ctypes.sizeof(self.module_list) == d.location.data_size 678 DebugPrint(self.module_list) 679 elif d.stream_type == MD_MEMORY_LIST_STREAM: 680 print >>sys.stderr, "Warning: This is not a full minidump!" 681 assert self.memory_list is None 682 self.memory_list = MINIDUMP_MEMORY_LIST.Read( 683 self.minidump, d.location.rva) 684 if ctypes.sizeof(self.memory_list) + 4 == d.location.data_size: 685 self.memory_list = MINIDUMP_MEMORY_LIST_Mac.Read( 686 self.minidump, d.location.rva) 687 assert ctypes.sizeof(self.memory_list) == d.location.data_size 688 DebugPrint(self.memory_list) 689 elif d.stream_type == MD_MEMORY_64_LIST_STREAM: 690 assert self.memory_list64 is None 691 self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read( 692 self.minidump, d.location.rva) 693 assert ctypes.sizeof(self.memory_list64) == d.location.data_size 694 DebugPrint(self.memory_list64) 695 696 def _FindObjdump(self, options): 697 if options.objdump: 698 objdump_bin = options.objdump 699 else: 700 objdump_bin = self._FindThirdPartyObjdump() 701 if not objdump_bin or not os.path.exists(objdump_bin): 702 print "# Cannot find '%s', falling back to default objdump '%s'" % ( 703 objdump_bin, DEFAULT_OBJDUMP_BIN) 704 objdump_bin = DEFAULT_OBJDUMP_BIN 705 global OBJDUMP_BIN 706 OBJDUMP_BIN = objdump_bin 707 disasm.OBJDUMP_BIN = objdump_bin 708 709 def _FindThirdPartyObjdump(self): 710 # Try to find the platform specific objdump 711 third_party_dir = os.path.join( 712 os.path.dirname(os.path.dirname(__file__)), 'third_party') 713 objdumps = [] 714 for root, dirs, files in os.walk(third_party_dir): 715 for file in files: 716 if file.endswith("objdump"): 717 objdumps.append(os.path.join(root, file)) 718 if self.arch == MD_CPU_ARCHITECTURE_ARM: 719 platform_filter = 'arm-linux' 720 elif self.arch == MD_CPU_ARCHITECTURE_ARM64: 721 platform_filter = 'aarch64' 722 else: 723 # use default otherwise 724 return None 725 print ("# Looking for platform specific (%s) objdump in " 726 "third_party directory.") % platform_filter 727 objdumps = filter(lambda file: platform_filter in file >= 0, objdumps) 728 if len(objdumps) == 0: 729 print "# Could not find platform specific objdump in third_party." 730 print "# Make sure you installed the correct SDK." 731 return None 732 return objdumps[0] 733 734 def ContextDescriptor(self): 735 if self.arch == MD_CPU_ARCHITECTURE_X86: 736 return MINIDUMP_CONTEXT_X86 737 elif self.arch == MD_CPU_ARCHITECTURE_AMD64: 738 return MINIDUMP_CONTEXT_AMD64 739 elif self.arch == MD_CPU_ARCHITECTURE_ARM: 740 return MINIDUMP_CONTEXT_ARM 741 elif self.arch == MD_CPU_ARCHITECTURE_ARM64: 742 return MINIDUMP_CONTEXT_ARM64 743 else: 744 return None 745 746 def IsValidAlignedAddress(self, address): 747 return self.IsAlignedAddress(address) and self.IsValidAddress(address) 748 749 def IsValidAddress(self, address): 750 return self.FindLocation(address) is not None 751 752 def IsAlignedAddress(self, address): 753 return (address % self.PointerSize()) == 0 754 755 def IsExceptionStackAddress(self, address): 756 if not self.IsAlignedAddress(address): return False 757 return self.IsAnyExceptionStackAddress(address) 758 759 def IsAnyExceptionStackAddress(self, address): 760 return self.StackTop() <= address <= self.StackBottom() 761 762 def IsValidExceptionStackAddress(self, address): 763 if not self.IsValidAddress(address): return False 764 return self.IsExceptionStackAddress(address) 765 766 def IsModuleAddress(self, address): 767 return self.GetModuleForAddress(address) != None 768 769 def GetModuleForAddress(self, address): 770 for module in self.module_list.modules: 771 start = module.base_of_image 772 end = start + module.size_of_image 773 if start <= address < end: return module 774 return None 775 776 def ReadU8(self, address): 777 location = self.FindLocation(address) 778 return ctypes.c_uint8.from_buffer(self.minidump, location).value 779 780 def ReadU32(self, address): 781 location = self.FindLocation(address) 782 return ctypes.c_uint32.from_buffer(self.minidump, location).value 783 784 def ReadU64(self, address): 785 location = self.FindLocation(address) 786 return ctypes.c_uint64.from_buffer(self.minidump, location).value 787 788 def Is64(self): 789 return (self.arch == MD_CPU_ARCHITECTURE_ARM64 or 790 self.arch == MD_CPU_ARCHITECTURE_AMD64) 791 792 def ReadUIntPtr(self, address): 793 if self.Is64(): 794 return self.ReadU64(address) 795 return self.ReadU32(address) 796 797 def ReadBytes(self, address, size): 798 location = self.FindLocation(address) 799 return self.minidump[location:location + size] 800 801 def _ReadWord(self, location): 802 if self.Is64(): 803 return ctypes.c_uint64.from_buffer(self.minidump, location).value 804 return ctypes.c_uint32.from_buffer(self.minidump, location).value 805 806 def ReadAsciiPtr(self, address): 807 ascii_content = [c if c >= '\x20' and c < '\x7f' else '.' 808 for c in self.ReadBytes(address, self.PointerSize())] 809 return ''.join(ascii_content) 810 811 def ReadAsciiString(self, address): 812 string = "" 813 while self.IsValidAddress(address): 814 code = self.ReadU8(address) 815 if 0 < code < 128: 816 string += chr(code) 817 else: 818 break 819 address += 1 820 return string 821 822 def IsProbableASCIIRegion(self, location, length): 823 ascii_bytes = 0 824 non_ascii_bytes = 0 825 for i in xrange(length): 826 loc = location + i 827 byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value 828 if byte >= 0x7f: 829 non_ascii_bytes += 1 830 if byte < 0x20 and byte != 0: 831 non_ascii_bytes += 1 832 if byte < 0x7f and byte >= 0x20: 833 ascii_bytes += 1 834 if byte == 0xa: # newline 835 ascii_bytes += 1 836 if ascii_bytes * 10 <= length: 837 return False 838 if length > 0 and ascii_bytes > non_ascii_bytes * 7: 839 return True 840 if ascii_bytes > non_ascii_bytes * 3: 841 return None # Maybe 842 return False 843 844 def IsProbableExecutableRegion(self, location, length): 845 opcode_bytes = 0 846 sixty_four = self.Is64() 847 for i in xrange(length): 848 loc = location + i 849 byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value 850 if (byte == 0x8b or # mov 851 byte == 0x89 or # mov reg-reg 852 (byte & 0xf0) == 0x50 or # push/pop 853 (sixty_four and (byte & 0xf0) == 0x40) or # rex prefix 854 byte == 0xc3 or # return 855 byte == 0x74 or # jeq 856 byte == 0x84 or # jeq far 857 byte == 0x75 or # jne 858 byte == 0x85 or # jne far 859 byte == 0xe8 or # call 860 byte == 0xe9 or # jmp far 861 byte == 0xeb): # jmp near 862 opcode_bytes += 1 863 opcode_percent = (opcode_bytes * 100) / length 864 threshold = 20 865 if opcode_percent > threshold + 2: 866 return True 867 if opcode_percent > threshold - 2: 868 return None # Maybe 869 return False 870 871 def FindRegion(self, addr): 872 answer = [-1, -1] 873 def is_in(reader, start, size, location): 874 if addr >= start and addr < start + size: 875 answer[0] = start 876 answer[1] = size 877 self.ForEachMemoryRegion(is_in) 878 if answer[0] == -1: 879 return None 880 return answer 881 882 def ForEachMemoryRegion(self, cb): 883 if self.memory_list64 is not None: 884 for r in self.memory_list64.ranges: 885 location = self.memory_list64.base_rva + offset 886 cb(self, r.start, r.size, location) 887 offset += r.size 888 889 if self.memory_list is not None: 890 for r in self.memory_list.ranges: 891 cb(self, r.start, r.memory.data_size, r.memory.rva) 892 893 def FindWord(self, word, alignment=0): 894 def search_inside_region(reader, start, size, location): 895 location = (location + alignment) & ~alignment 896 for i in xrange(size - self.PointerSize()): 897 loc = location + i 898 if reader._ReadWord(loc) == word: 899 slot = start + (loc - location) 900 print "%s: %s" % (reader.FormatIntPtr(slot), 901 reader.FormatIntPtr(word)) 902 self.ForEachMemoryRegion(search_inside_region) 903 904 def FindWordList(self, word): 905 aligned_res = [] 906 unaligned_res = [] 907 def search_inside_region(reader, start, size, location): 908 for i in xrange(size - self.PointerSize()): 909 loc = location + i 910 if reader._ReadWord(loc) == word: 911 slot = start + (loc - location) 912 if self.IsAlignedAddress(slot): 913 aligned_res.append(slot) 914 else: 915 unaligned_res.append(slot) 916 self.ForEachMemoryRegion(search_inside_region) 917 return (aligned_res, unaligned_res) 918 919 def FindLocation(self, address): 920 offset = 0 921 if self.memory_list64 is not None: 922 for r in self.memory_list64.ranges: 923 if r.start <= address < r.start + r.size: 924 return self.memory_list64.base_rva + offset + address - r.start 925 offset += r.size 926 if self.memory_list is not None: 927 for r in self.memory_list.ranges: 928 if r.start <= address < r.start + r.memory.data_size: 929 return r.memory.rva + address - r.start 930 return None 931 932 def GetDisasmLines(self, address, size): 933 def CountUndefinedInstructions(lines): 934 pattern = "<UNDEFINED>" 935 return sum([line.count(pattern) for (ignore, line) in lines]) 936 937 location = self.FindLocation(address) 938 if location is None: return [] 939 arch = None 940 possible_objdump_flags = [""] 941 if self.arch == MD_CPU_ARCHITECTURE_X86: 942 arch = "ia32" 943 elif self.arch == MD_CPU_ARCHITECTURE_ARM: 944 arch = "arm" 945 possible_objdump_flags = ["", "--disassembler-options=force-thumb"] 946 elif self.arch == MD_CPU_ARCHITECTURE_ARM64: 947 arch = "arm64" 948 possible_objdump_flags = ["", "--disassembler-options=force-thumb"] 949 elif self.arch == MD_CPU_ARCHITECTURE_AMD64: 950 arch = "x64" 951 results = [ disasm.GetDisasmLines(self.minidump_name, 952 location, 953 size, 954 arch, 955 False, 956 objdump_flags) 957 for objdump_flags in possible_objdump_flags ] 958 return min(results, key=CountUndefinedInstructions) 959 960 961 def Dispose(self): 962 self.minidump.close() 963 self.minidump_file.close() 964 965 def ExceptionIP(self): 966 if self.arch == MD_CPU_ARCHITECTURE_AMD64: 967 return self.exception_context.rip 968 elif self.arch == MD_CPU_ARCHITECTURE_ARM: 969 return self.exception_context.pc 970 elif self.arch == MD_CPU_ARCHITECTURE_ARM64: 971 return self.exception_context.pc 972 elif self.arch == MD_CPU_ARCHITECTURE_X86: 973 return self.exception_context.eip 974 975 def ExceptionSP(self): 976 if self.arch == MD_CPU_ARCHITECTURE_AMD64: 977 return self.exception_context.rsp 978 elif self.arch == MD_CPU_ARCHITECTURE_ARM: 979 return self.exception_context.sp 980 elif self.arch == MD_CPU_ARCHITECTURE_ARM64: 981 return self.exception_context.sp 982 elif self.arch == MD_CPU_ARCHITECTURE_X86: 983 return self.exception_context.esp 984 985 def ExceptionFP(self): 986 if self.arch == MD_CPU_ARCHITECTURE_AMD64: 987 return self.exception_context.rbp 988 elif self.arch == MD_CPU_ARCHITECTURE_ARM: 989 return None 990 elif self.arch == MD_CPU_ARCHITECTURE_ARM64: 991 return self.exception_context.fp 992 elif self.arch == MD_CPU_ARCHITECTURE_X86: 993 return self.exception_context.ebp 994 995 def ExceptionThread(self): 996 return self.thread_map[self.exception.thread_id] 997 998 def StackTop(self): 999 return self.ExceptionSP() 1000 1001 def StackBottom(self): 1002 exception_thread = self.ExceptionThread() 1003 return exception_thread.stack.start + \ 1004 exception_thread.stack.memory.data_size 1005 1006 def FormatIntPtr(self, value): 1007 if self.Is64(): 1008 return "%016x" % value 1009 return "%08x" % value 1010 1011 def PointerSize(self): 1012 if self.Is64(): 1013 return 8 1014 return 4 1015 1016 def Register(self, name): 1017 return self.exception_context.__getattribute__(name) 1018 1019 def ReadMinidumpString(self, rva): 1020 string = bytearray(MINIDUMP_STRING.Read(self.minidump, rva).buffer) 1021 string = string.decode("utf16") 1022 return string[0:len(string) - 1] 1023 1024 # Load FUNC records from a BreakPad symbol file 1025 # 1026 # http://code.google.com/p/google-breakpad/wiki/SymbolFiles 1027 # 1028 def _LoadSymbolsFrom(self, symfile, baseaddr): 1029 print "Loading symbols from %s" % (symfile) 1030 funcs = [] 1031 with open(symfile) as f: 1032 for line in f: 1033 result = re.match( 1034 r"^FUNC ([a-f0-9]+) ([a-f0-9]+) ([a-f0-9]+) (.*)$", line) 1035 if result is not None: 1036 start = int(result.group(1), 16) 1037 size = int(result.group(2), 16) 1038 name = result.group(4).rstrip() 1039 bisect.insort_left(self.symbols, 1040 FuncSymbol(baseaddr + start, size, name)) 1041 print " ... done" 1042 1043 def TryLoadSymbolsFor(self, modulename, module): 1044 try: 1045 symfile = os.path.join(self.symdir, 1046 modulename.replace('.', '_') + ".pdb.sym") 1047 if os.path.isfile(symfile): 1048 self._LoadSymbolsFrom(symfile, module.base_of_image) 1049 self.modules_with_symbols.append(module) 1050 except Exception as e: 1051 print " ... failure (%s)" % (e) 1052 1053 # Returns true if address is covered by some module that has loaded symbols. 1054 def _IsInModuleWithSymbols(self, addr): 1055 for module in self.modules_with_symbols: 1056 start = module.base_of_image 1057 end = start + module.size_of_image 1058 if (start <= addr) and (addr < end): 1059 return True 1060 return False 1061 1062 # Find symbol covering the given address and return its name in format 1063 # <symbol name>+<offset from the start> 1064 def FindSymbol(self, addr): 1065 if not self._IsInModuleWithSymbols(addr): 1066 return None 1067 1068 i = bisect.bisect_left(self.symbols, addr) 1069 symbol = None 1070 if (0 < i) and self.symbols[i - 1].Covers(addr): 1071 symbol = self.symbols[i - 1] 1072 elif (i < len(self.symbols)) and self.symbols[i].Covers(addr): 1073 symbol = self.symbols[i] 1074 else: 1075 return None 1076 diff = addr - symbol.start 1077 return "%s+0x%x" % (symbol.name, diff) 1078 1079 1080class Printer(object): 1081 """Printer with indentation support.""" 1082 1083 def __init__(self): 1084 self.indent = 0 1085 1086 def Indent(self): 1087 self.indent += 2 1088 1089 def Dedent(self): 1090 self.indent -= 2 1091 1092 def Print(self, string): 1093 print "%s%s" % (self._IndentString(), string) 1094 1095 def PrintLines(self, lines): 1096 indent = self._IndentString() 1097 print "\n".join("%s%s" % (indent, line) for line in lines) 1098 1099 def _IndentString(self): 1100 return self.indent * " " 1101 1102 1103ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+") 1104 1105 1106def FormatDisasmLine(start, heap, line): 1107 line_address = start + line[0] 1108 stack_slot = heap.stack_map.get(line_address) 1109 marker = " " 1110 if stack_slot: 1111 marker = "=>" 1112 code = AnnotateAddresses(heap, line[1]) 1113 1114 # Compute the actual call target which the disassembler is too stupid 1115 # to figure out (it adds the call offset to the disassembly offset rather 1116 # than the absolute instruction address). 1117 if heap.reader.arch == MD_CPU_ARCHITECTURE_X86: 1118 if code.startswith("e8"): 1119 words = code.split() 1120 if len(words) > 6 and words[5] == "call": 1121 offset = int(words[4] + words[3] + words[2] + words[1], 16) 1122 target = (line_address + offset + 5) & 0xFFFFFFFF 1123 code = code.replace(words[6], "0x%08x" % target) 1124 # TODO(jkummerow): port this hack to ARM and x64. 1125 1126 return "%s%08x %08x: %s" % (marker, line_address, line[0], code) 1127 1128 1129def AnnotateAddresses(heap, line): 1130 extra = [] 1131 for m in ADDRESS_RE.finditer(line): 1132 maybe_address = int(m.group(0), 16) 1133 object = heap.FindObject(maybe_address) 1134 if not object: continue 1135 extra.append(str(object)) 1136 if len(extra) == 0: return line 1137 return "%s ;; %s" % (line, ", ".join(extra)) 1138 1139 1140class HeapObject(object): 1141 def __init__(self, heap, map, address): 1142 self.heap = heap 1143 self.map = map 1144 self.address = address 1145 1146 def Is(self, cls): 1147 return isinstance(self, cls) 1148 1149 def Print(self, p): 1150 p.Print(str(self)) 1151 1152 def __str__(self): 1153 instance_type = "???" 1154 if self.map is not None: 1155 instance_type = INSTANCE_TYPES[self.map.instance_type] 1156 return "%s(%s, %s)" % (self.__class__.__name__, 1157 self.heap.reader.FormatIntPtr(self.address), 1158 instance_type) 1159 1160 def ObjectField(self, offset): 1161 field_value = self.heap.reader.ReadUIntPtr(self.address + offset) 1162 return self.heap.FindObjectOrSmi(field_value) 1163 1164 def SmiField(self, offset): 1165 field_value = self.heap.reader.ReadUIntPtr(self.address + offset) 1166 if self.heap.IsSmi(field_value): 1167 return self.heap.SmiUntag(field_value) 1168 return None 1169 1170 1171class Map(HeapObject): 1172 def Decode(self, offset, size, value): 1173 return (value >> offset) & ((1 << size) - 1) 1174 1175 # Instance Sizes 1176 def InstanceSizesOffset(self): 1177 return self.heap.PointerSize() 1178 1179 def InstanceSizeOffset(self): 1180 return self.InstanceSizesOffset() 1181 1182 def InObjectProperties(self): 1183 return self.InstanceSizeOffset() + 1 1184 1185 def UnusedByte(self): 1186 return self.InObjectProperties() + 1 1187 1188 def VisitorId(self): 1189 return self.UnusedByte() + 1 1190 1191 # Instance Attributes 1192 def InstanceAttributesOffset(self): 1193 return self.InstanceSizesOffset() + self.heap.IntSize() 1194 1195 def InstanceTypeOffset(self): 1196 return self.InstanceAttributesOffset() 1197 1198 def BitFieldOffset(self): 1199 return self.InstanceTypeOffset() + 1 1200 1201 def BitField2Offset(self): 1202 return self.BitFieldOffset() + 1 1203 1204 def UnusedPropertyFieldsOffset(self): 1205 return self.BitField2Offset() + 1 1206 1207 # Other fields 1208 def BitField3Offset(self): 1209 return self.InstanceAttributesOffset() + self.heap.IntSize() 1210 1211 def PrototypeOffset(self): 1212 return self.BitField3Offset() + self.heap.PointerSize() 1213 1214 def ConstructorOrBackPointerOffset(self): 1215 return self.PrototypeOffset() + self.heap.PointerSize() 1216 1217 def TransitionsOrPrototypeInfoOffset(self): 1218 return self.ConstructorOrBackPointerOffset() + self.heap.PointerSize() 1219 1220 def DescriptorsOffset(self): 1221 return self.TransitionsOrPrototypeInfoOffset() + self.heap.PointerSize() 1222 1223 def LayoutDescriptorOffset(self): 1224 return self.DescriptorsOffset() + self.heap.PointerSize() 1225 1226 def CodeCacheOffset(self): 1227 if (self.heap.reader.Is64()): 1228 return self.LayoutDescriptorOffset() + self.heap.PointerSize() 1229 return self.DescriptorsOffset() + self.heap.PointerSize() 1230 1231 def DependentCodeOffset(self): 1232 return self.CodeCacheOffset() + self.heap.PointerSize() 1233 1234 def ReadByte(self, offset): 1235 return self.heap.reader.ReadU8(self.address + offset) 1236 1237 def ReadWord(self, offset): 1238 return self.heap.reader.ReadUIntPtr(self.address + offset) 1239 1240 def Print(self, p): 1241 p.Print("Map(%08x)" % (self.address)) 1242 p.Print(" - size: %d, inobject: %d, (unused: %d), visitor: %d" % ( 1243 self.ReadByte(self.InstanceSizeOffset()), 1244 self.ReadByte(self.InObjectProperties()), 1245 self.ReadByte(self.UnusedByte()), 1246 self.VisitorId())) 1247 1248 instance_type = INSTANCE_TYPES[self.ReadByte(self.InstanceTypeOffset())] 1249 bitfield = self.ReadByte(self.BitFieldOffset()) 1250 bitfield2 = self.ReadByte(self.BitField2Offset()) 1251 unused = self.ReadByte(self.UnusedPropertyFieldsOffset()) 1252 p.Print(" - %s, bf: %d, bf2: %d, unused: %d" % ( 1253 instance_type, bitfield, bitfield2, unused)) 1254 1255 p.Print(" - kind: %s" % (self.Decode(3, 5, bitfield2))) 1256 1257 bitfield3 = self.ReadWord(self.BitField3Offset()) 1258 1259 p.Print( 1260 " - EnumLength: %d NumberOfOwnDescriptors: %d OwnsDescriptors: %s" % ( 1261 self.Decode(0, 10, bitfield3), 1262 self.Decode(10, 10, bitfield3), 1263 self.Decode(21, 1, bitfield3))) 1264 p.Print(" - DictionaryMap: %s" % (self.Decode(20, 1, bitfield3))) 1265 p.Print(" - Deprecated: %s" % (self.Decode(23, 1, bitfield3))) 1266 p.Print(" - IsUnstable: %s" % (self.Decode(24, 1, bitfield3))) 1267 p.Print(" - NewTargetIsBase: %s" % (self.Decode(27, 1, bitfield3))) 1268 1269 descriptors = self.ObjectField(self.DescriptorsOffset()) 1270 if descriptors.__class__ == FixedArray: 1271 DescriptorArray(descriptors).Print(p) 1272 else: 1273 p.Print(" - Descriptors: %s" % (descriptors)) 1274 1275 transitions = self.ObjectField(self.TransitionsOrPrototypeInfoOffset()) 1276 if transitions.__class__ == FixedArray: 1277 TransitionArray(transitions).Print(p) 1278 else: 1279 p.Print(" - TransitionsOrPrototypeInfo: %s" % (transitions)) 1280 1281 p.Print(" - Prototype: %s" % self.ObjectField(self.PrototypeOffset())) 1282 1283 def __init__(self, heap, map, address): 1284 HeapObject.__init__(self, heap, map, address) 1285 self.instance_type = \ 1286 heap.reader.ReadU8(self.address + self.InstanceTypeOffset()) 1287 1288 1289class String(HeapObject): 1290 def LengthOffset(self): 1291 # First word after the map is the hash, the second is the length. 1292 return self.heap.PointerSize() * 2 1293 1294 def __init__(self, heap, map, address): 1295 HeapObject.__init__(self, heap, map, address) 1296 self.length = self.SmiField(self.LengthOffset()) 1297 1298 def GetChars(self): 1299 return "?string?" 1300 1301 def Print(self, p): 1302 p.Print(str(self)) 1303 1304 def __str__(self): 1305 return "\"%s\"" % self.GetChars() 1306 1307 1308class SeqString(String): 1309 def CharsOffset(self): 1310 return self.heap.PointerSize() * 3 1311 1312 def __init__(self, heap, map, address): 1313 String.__init__(self, heap, map, address) 1314 self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(), 1315 self.length) 1316 1317 def GetChars(self): 1318 return self.chars 1319 1320 1321class ExternalString(String): 1322 # TODO(vegorov) fix ExternalString for X64 architecture 1323 RESOURCE_OFFSET = 12 1324 1325 WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4 1326 WEBKIT_STRING_IMPL_CHARS_OFFSET = 8 1327 1328 def __init__(self, heap, map, address): 1329 String.__init__(self, heap, map, address) 1330 reader = heap.reader 1331 self.resource = \ 1332 reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET) 1333 self.chars = "?external string?" 1334 if not reader.IsValidAddress(self.resource): return 1335 string_impl_address = self.resource + \ 1336 ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET 1337 if not reader.IsValidAddress(string_impl_address): return 1338 string_impl = reader.ReadU32(string_impl_address) 1339 chars_ptr_address = string_impl + \ 1340 ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET 1341 if not reader.IsValidAddress(chars_ptr_address): return 1342 chars_ptr = reader.ReadU32(chars_ptr_address) 1343 if not reader.IsValidAddress(chars_ptr): return 1344 raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length) 1345 self.chars = codecs.getdecoder("utf16")(raw_chars)[0] 1346 1347 def GetChars(self): 1348 return self.chars 1349 1350 1351class ConsString(String): 1352 def LeftOffset(self): 1353 return self.heap.PointerSize() * 3 1354 1355 def RightOffset(self): 1356 return self.heap.PointerSize() * 4 1357 1358 def __init__(self, heap, map, address): 1359 String.__init__(self, heap, map, address) 1360 self.left = self.ObjectField(self.LeftOffset()) 1361 self.right = self.ObjectField(self.RightOffset()) 1362 1363 def GetChars(self): 1364 try: 1365 return self.left.GetChars() + self.right.GetChars() 1366 except: 1367 return "***CAUGHT EXCEPTION IN GROKDUMP***" 1368 1369 1370class Oddball(HeapObject): 1371 #Should match declarations in objects.h 1372 KINDS = [ 1373 "False", 1374 "True", 1375 "TheHole", 1376 "Null", 1377 "ArgumentMarker", 1378 "Undefined", 1379 "Other" 1380 ] 1381 1382 def ToStringOffset(self): 1383 return self.heap.PointerSize() 1384 1385 def ToNumberOffset(self): 1386 return self.ToStringOffset() + self.heap.PointerSize() 1387 1388 def KindOffset(self): 1389 return self.ToNumberOffset() + self.heap.PointerSize() 1390 1391 def __init__(self, heap, map, address): 1392 HeapObject.__init__(self, heap, map, address) 1393 self.to_string = self.ObjectField(self.ToStringOffset()) 1394 self.kind = self.SmiField(self.KindOffset()) 1395 1396 def Print(self, p): 1397 p.Print(str(self)) 1398 1399 def __str__(self): 1400 if self.to_string: 1401 return "Oddball(%08x, <%s>)" % (self.address, str(self.to_string)) 1402 else: 1403 kind = "???" 1404 if 0 <= self.kind < len(Oddball.KINDS): 1405 kind = Oddball.KINDS[self.kind] 1406 return "Oddball(%08x, kind=%s)" % (self.address, kind) 1407 1408 1409class FixedArray(HeapObject): 1410 def LengthOffset(self): 1411 return self.heap.PointerSize() 1412 1413 def ElementsOffset(self): 1414 return self.heap.PointerSize() * 2 1415 1416 def MemberOffset(self, i): 1417 return self.ElementsOffset() + self.heap.PointerSize() * i 1418 1419 def Get(self, i): 1420 return self.ObjectField(self.MemberOffset(i)) 1421 1422 def __init__(self, heap, map, address): 1423 HeapObject.__init__(self, heap, map, address) 1424 self.length = self.SmiField(self.LengthOffset()) 1425 1426 def Print(self, p): 1427 p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address)) 1428 p.Indent() 1429 p.Print("length: %d" % self.length) 1430 base_offset = self.ElementsOffset() 1431 for i in xrange(self.length): 1432 offset = base_offset + 4 * i 1433 try: 1434 p.Print("[%08d] = %s" % (i, self.ObjectField(offset))) 1435 except TypeError: 1436 p.Dedent() 1437 p.Print("...") 1438 p.Print("}") 1439 return 1440 p.Dedent() 1441 p.Print("}") 1442 1443 def __str__(self): 1444 return "FixedArray(%08x, length=%d)" % (self.address, self.length) 1445 1446 1447class DescriptorArray(object): 1448 def __init__(self, array): 1449 self.array = array 1450 1451 def Length(self): 1452 return self.array.Get(0) 1453 1454 def Decode(self, offset, size, value): 1455 return (value >> offset) & ((1 << size) - 1) 1456 1457 TYPES = [ 1458 "normal", 1459 "field", 1460 "function", 1461 "callbacks" 1462 ] 1463 1464 def Type(self, value): 1465 return DescriptorArray.TYPES[self.Decode(0, 3, value)] 1466 1467 def Attributes(self, value): 1468 attributes = self.Decode(3, 3, value) 1469 result = [] 1470 if (attributes & 0): result += ["ReadOnly"] 1471 if (attributes & 1): result += ["DontEnum"] 1472 if (attributes & 2): result += ["DontDelete"] 1473 return "[" + (",".join(result)) + "]" 1474 1475 def Deleted(self, value): 1476 return self.Decode(6, 1, value) == 1 1477 1478 def FieldIndex(self, value): 1479 return self.Decode(20, 11, value) 1480 1481 def Pointer(self, value): 1482 return self.Decode(6, 11, value) 1483 1484 def Details(self, di, value): 1485 return ( 1486 di, 1487 self.Type(value), 1488 self.Attributes(value), 1489 self.FieldIndex(value), 1490 self.Pointer(value) 1491 ) 1492 1493 1494 def Print(self, p): 1495 length = self.Length() 1496 array = self.array 1497 1498 p.Print("Descriptors(%08x, length=%d)" % (array.address, length)) 1499 p.Print("[et] %s" % (array.Get(1))) 1500 1501 for di in xrange(length): 1502 i = 2 + di * 3 1503 p.Print("0x%x" % (array.address + array.MemberOffset(i))) 1504 p.Print("[%i] name: %s" % (di, array.Get(i + 0))) 1505 p.Print("[%i] details: %s %s field-index %i pointer %i" % \ 1506 self.Details(di, array.Get(i + 1))) 1507 p.Print("[%i] value: %s" % (di, array.Get(i + 2))) 1508 1509 end = self.array.length // 3 1510 if length != end: 1511 p.Print("[%i-%i] slack descriptors" % (length, end)) 1512 1513 1514class TransitionArray(object): 1515 def __init__(self, array): 1516 self.array = array 1517 1518 def IsSimpleTransition(self): 1519 return self.array.length <= 2 1520 1521 def Length(self): 1522 # SimpleTransition cases 1523 if self.IsSimpleTransition(): 1524 return self.array.length - 1 1525 return (self.array.length - 3) // 2 1526 1527 def Print(self, p): 1528 length = self.Length() 1529 array = self.array 1530 1531 p.Print("Transitions(%08x, length=%d)" % (array.address, length)) 1532 p.Print("[backpointer] %s" % (array.Get(0))) 1533 if self.IsSimpleTransition(): 1534 if length == 1: 1535 p.Print("[simple target] %s" % (array.Get(1))) 1536 return 1537 1538 elements = array.Get(1) 1539 if elements is not None: 1540 p.Print("[elements ] %s" % (elements)) 1541 1542 prototype = array.Get(2) 1543 if prototype is not None: 1544 p.Print("[prototype ] %s" % (prototype)) 1545 1546 for di in xrange(length): 1547 i = 3 + di * 2 1548 p.Print("[%i] symbol: %s" % (di, array.Get(i + 0))) 1549 p.Print("[%i] target: %s" % (di, array.Get(i + 1))) 1550 1551 1552class JSFunction(HeapObject): 1553 def CodeEntryOffset(self): 1554 return 3 * self.heap.PointerSize() 1555 1556 def SharedOffset(self): 1557 return 5 * self.heap.PointerSize() 1558 1559 def __init__(self, heap, map, address): 1560 HeapObject.__init__(self, heap, map, address) 1561 code_entry = \ 1562 heap.reader.ReadU32(self.address + self.CodeEntryOffset()) 1563 self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1) 1564 self.shared = self.ObjectField(self.SharedOffset()) 1565 1566 def Print(self, p): 1567 source = "\n".join(" %s" % line for line in self._GetSource().split("\n")) 1568 p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address)) 1569 p.Indent() 1570 p.Print("inferred name: %s" % self.shared.inferred_name) 1571 if self.shared.script.Is(Script) and self.shared.script.name.Is(String): 1572 p.Print("script name: %s" % self.shared.script.name) 1573 p.Print("source:") 1574 p.PrintLines(self._GetSource().split("\n")) 1575 p.Print("code:") 1576 self.code.Print(p) 1577 if self.code != self.shared.code: 1578 p.Print("unoptimized code:") 1579 self.shared.code.Print(p) 1580 p.Dedent() 1581 p.Print("}") 1582 1583 def __str__(self): 1584 inferred_name = "" 1585 if self.shared is not None and self.shared.Is(SharedFunctionInfo): 1586 inferred_name = self.shared.inferred_name 1587 return "JSFunction(%s, %s) " % \ 1588 (self.heap.reader.FormatIntPtr(self.address), inferred_name) 1589 1590 def _GetSource(self): 1591 source = "?source?" 1592 start = self.shared.start_position 1593 end = self.shared.end_position 1594 if not self.shared.script.Is(Script): return source 1595 script_source = self.shared.script.source 1596 if not script_source.Is(String): return source 1597 if start and end: 1598 source = script_source.GetChars()[start:end] 1599 return source 1600 1601 1602class SharedFunctionInfo(HeapObject): 1603 def CodeOffset(self): 1604 return 2 * self.heap.PointerSize() 1605 1606 def ScriptOffset(self): 1607 return 7 * self.heap.PointerSize() 1608 1609 def InferredNameOffset(self): 1610 return 9 * self.heap.PointerSize() 1611 1612 def EndPositionOffset(self): 1613 return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize() 1614 1615 def StartPositionAndTypeOffset(self): 1616 return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize() 1617 1618 def __init__(self, heap, map, address): 1619 HeapObject.__init__(self, heap, map, address) 1620 try: 1621 self.code = self.ObjectField(self.CodeOffset()) 1622 self.script = self.ObjectField(self.ScriptOffset()) 1623 self.inferred_name = self.ObjectField(self.InferredNameOffset()) 1624 if heap.PointerSize() == 8: 1625 start_position_and_type = \ 1626 heap.reader.ReadU32(self.StartPositionAndTypeOffset()) 1627 self.start_position = start_position_and_type >> 2 1628 pseudo_smi_end_position = \ 1629 heap.reader.ReadU32(self.EndPositionOffset()) 1630 self.end_position = pseudo_smi_end_position >> 2 1631 else: 1632 start_position_and_type = \ 1633 self.SmiField(self.StartPositionAndTypeOffset()) 1634 if start_position_and_type: 1635 self.start_position = start_position_and_type >> 2 1636 else: 1637 self.start_position = None 1638 self.end_position = \ 1639 self.SmiField(self.EndPositionOffset()) 1640 except: 1641 print("*** Error while reading SharedFunctionInfo") 1642 1643 1644class Script(HeapObject): 1645 def SourceOffset(self): 1646 return self.heap.PointerSize() 1647 1648 def NameOffset(self): 1649 return self.SourceOffset() + self.heap.PointerSize() 1650 1651 def __init__(self, heap, map, address): 1652 HeapObject.__init__(self, heap, map, address) 1653 self.source = self.ObjectField(self.SourceOffset()) 1654 self.name = self.ObjectField(self.NameOffset()) 1655 1656 1657class CodeCache(HeapObject): 1658 def DefaultCacheOffset(self): 1659 return self.heap.PointerSize() 1660 1661 def NormalTypeCacheOffset(self): 1662 return self.DefaultCacheOffset() + self.heap.PointerSize() 1663 1664 def __init__(self, heap, map, address): 1665 HeapObject.__init__(self, heap, map, address) 1666 self.default_cache = self.ObjectField(self.DefaultCacheOffset()) 1667 self.normal_type_cache = self.ObjectField(self.NormalTypeCacheOffset()) 1668 1669 def Print(self, p): 1670 p.Print("CodeCache(%s) {" % self.heap.reader.FormatIntPtr(self.address)) 1671 p.Indent() 1672 p.Print("default cache: %s" % self.default_cache) 1673 p.Print("normal type cache: %s" % self.normal_type_cache) 1674 p.Dedent() 1675 p.Print("}") 1676 1677 1678class Code(HeapObject): 1679 CODE_ALIGNMENT_MASK = (1 << 5) - 1 1680 1681 def InstructionSizeOffset(self): 1682 return self.heap.PointerSize() 1683 1684 @staticmethod 1685 def HeaderSize(heap): 1686 return (heap.PointerSize() + heap.IntSize() + \ 1687 4 * heap.PointerSize() + 3 * heap.IntSize() + \ 1688 Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK 1689 1690 def __init__(self, heap, map, address): 1691 HeapObject.__init__(self, heap, map, address) 1692 self.entry = self.address + Code.HeaderSize(heap) 1693 self.instruction_size = \ 1694 heap.reader.ReadU32(self.address + self.InstructionSizeOffset()) 1695 1696 def Print(self, p): 1697 lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size) 1698 p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address)) 1699 p.Indent() 1700 p.Print("instruction_size: %d" % self.instruction_size) 1701 p.PrintLines(self._FormatLine(line) for line in lines) 1702 p.Dedent() 1703 p.Print("}") 1704 1705 def _FormatLine(self, line): 1706 return FormatDisasmLine(self.entry, self.heap, line) 1707 1708 1709class V8Heap(object): 1710 CLASS_MAP = { 1711 "SYMBOL_TYPE": SeqString, 1712 "ONE_BYTE_SYMBOL_TYPE": SeqString, 1713 "CONS_SYMBOL_TYPE": ConsString, 1714 "CONS_ONE_BYTE_SYMBOL_TYPE": ConsString, 1715 "EXTERNAL_SYMBOL_TYPE": ExternalString, 1716 "EXTERNAL_SYMBOL_WITH_ONE_BYTE_DATA_TYPE": ExternalString, 1717 "EXTERNAL_ONE_BYTE_SYMBOL_TYPE": ExternalString, 1718 "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString, 1719 "SHORT_EXTERNAL_SYMBOL_WITH_ONE_BYTE_DATA_TYPE": ExternalString, 1720 "SHORT_EXTERNAL_ONE_BYTE_SYMBOL_TYPE": ExternalString, 1721 "STRING_TYPE": SeqString, 1722 "ONE_BYTE_STRING_TYPE": SeqString, 1723 "CONS_STRING_TYPE": ConsString, 1724 "CONS_ONE_BYTE_STRING_TYPE": ConsString, 1725 "EXTERNAL_STRING_TYPE": ExternalString, 1726 "EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE": ExternalString, 1727 "EXTERNAL_ONE_BYTE_STRING_TYPE": ExternalString, 1728 "MAP_TYPE": Map, 1729 "ODDBALL_TYPE": Oddball, 1730 "FIXED_ARRAY_TYPE": FixedArray, 1731 "HASH_TABLE_TYPE": FixedArray, 1732 "OBJECT_BOILERPLATE_DESCRIPTION_TYPE": FixedArray, 1733 "SCOPE_INFO_TYPE": FixedArray, 1734 "JS_FUNCTION_TYPE": JSFunction, 1735 "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo, 1736 "SCRIPT_TYPE": Script, 1737 "CODE_CACHE_TYPE": CodeCache, 1738 "CODE_TYPE": Code, 1739 } 1740 1741 def __init__(self, reader, stack_map): 1742 self.reader = reader 1743 self.stack_map = stack_map 1744 self.objects = {} 1745 1746 def FindObjectOrSmi(self, tagged_address): 1747 if self.IsSmi(tagged_address): return self.SmiUntag(tagged_address) 1748 return self.FindObject(tagged_address) 1749 1750 def FindObject(self, tagged_address): 1751 if tagged_address in self.objects: 1752 return self.objects[tagged_address] 1753 if not self.IsTaggedObjectAddress(tagged_address): return None 1754 address = tagged_address - 1 1755 if not self.reader.IsValidAddress(address): return None 1756 map_tagged_address = self.reader.ReadUIntPtr(address) 1757 if tagged_address == map_tagged_address: 1758 # Meta map? 1759 meta_map = Map(self, None, address) 1760 instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type) 1761 if instance_type_name != "MAP_TYPE": return None 1762 meta_map.map = meta_map 1763 object = meta_map 1764 else: 1765 map = self.FindMap(map_tagged_address) 1766 if map is None: return None 1767 instance_type_name = INSTANCE_TYPES.get(map.instance_type) 1768 if instance_type_name is None: return None 1769 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject) 1770 object = cls(self, map, address) 1771 self.objects[tagged_address] = object 1772 return object 1773 1774 def FindMap(self, tagged_address): 1775 address = self.FindMapAddress(tagged_address) 1776 if not address: return None 1777 object = Map(self, None, address) 1778 return object 1779 1780 def FindMapAddress(self, tagged_address): 1781 if not self.IsTaggedMapAddress(tagged_address): return None 1782 address = tagged_address - 1 1783 if not self.reader.IsValidAddress(address): return None 1784 return address 1785 1786 def IntSize(self): 1787 return 4 1788 1789 def PointerSize(self): 1790 return self.reader.PointerSize() 1791 1792 def ObjectAlignmentMask(self): 1793 return self.PointerSize() - 1 1794 1795 def IsTaggedObjectAddress(self, address): 1796 return (address & self.ObjectAlignmentMask()) == 1 1797 1798 def IsValidTaggedObjectAddress(self, address): 1799 if not self.IsTaggedObjectAddress(address): return False 1800 return self.reader.IsValidAddress(address) 1801 1802 def IsTaggedMapAddress(self, address): 1803 return (address & self.MapAlignmentMask()) == 1 1804 1805 def MapAlignmentMask(self): 1806 if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64: 1807 return (1 << 4) - 1 1808 elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM: 1809 return (1 << 4) - 1 1810 elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM64: 1811 return (1 << 4) - 1 1812 elif self.reader.arch == MD_CPU_ARCHITECTURE_X86: 1813 return (1 << 5) - 1 1814 1815 def PageAlignmentMask(self): 1816 return (1 << 19) - 1 1817 1818 def IsTaggedAddress(self, address): 1819 return (address & self.ObjectAlignmentMask()) == 1 1820 1821 def IsSmi(self, tagged_address): 1822 if self.reader.Is64(): 1823 return (tagged_address & 0xFFFFFFFF) == 0 1824 return not self.IsTaggedAddress(tagged_address) 1825 1826 def SmiUntag(self, tagged_address): 1827 if self.reader.Is64(): return tagged_address >> 32 1828 return tagged_address >> 1 1829 1830 def AddressTypeMarker(self, address): 1831 if not self.reader.IsValidAddress(address): return " " 1832 if self.reader.IsExceptionStackAddress(address): return "S" 1833 if self.reader.IsModuleAddress(address): return "C" 1834 if self.IsTaggedAddress(address): 1835 # Cannot have an tagged pointer into the stack 1836 if self.reader.IsAnyExceptionStackAddress(address): return "s" 1837 return "T" 1838 return "*" 1839 1840 def FormatIntPtr(self, address): 1841 marker = self.AddressTypeMarker(address) 1842 address = self.reader.FormatIntPtr(address) 1843 if marker == " ": return address 1844 return "%s %s" % (address, marker) 1845 1846 def RelativeOffset(self, slot, address): 1847 if not self.reader.IsValidAlignedAddress(slot): return None 1848 if self.IsTaggedObjectAddress(address): 1849 address -= 1 1850 if not self.reader.IsValidAlignedAddress(address): return None 1851 offset = (address - slot) / self.PointerSize() 1852 1853 lower_limit = -32 1854 upper_limit = 128 1855 if self.reader.IsExceptionStackAddress(address): 1856 upper_limit = 0xFFFFFF 1857 1858 if offset < lower_limit or upper_limit < offset: return None 1859 target_address = self.reader.ReadUIntPtr(address) 1860 return "[%+02d]=%s %s" % (offset, self.reader.FormatIntPtr(target_address), 1861 self.AddressTypeMarker(target_address)) 1862 1863 def FindObjectPointers(self, start=0, end=0): 1864 objects = set() 1865 def find_object_in_region(reader, start, size, location): 1866 for slot in range(start, start+size, self.reader.PointerSize()): 1867 if not self.reader.IsValidAddress(slot): break 1868 # Collect only tagged pointers (object) to tagged pointers (map) 1869 tagged_address = self.reader.ReadUIntPtr(slot) 1870 if not self.IsValidTaggedObjectAddress(tagged_address): continue 1871 map_address = self.reader.ReadUIntPtr(tagged_address - 1) 1872 if not self.IsTaggedMapAddress(map_address): continue 1873 objects.add(tagged_address) 1874 1875 if not start and not end: 1876 self.reader.ForEachMemoryRegion(find_object_in_region) 1877 else: 1878 find_object_in_region(self.reader, start, end-start, None) 1879 1880 return objects 1881 1882class KnownObject(HeapObject): 1883 def __init__(self, heap, known_name): 1884 HeapObject.__init__(self, heap, None, None) 1885 self.known_name = known_name 1886 1887 def __str__(self): 1888 return "<%s>" % self.known_name 1889 1890 1891class KnownMap(HeapObject): 1892 def __init__(self, heap, known_name, instance_type): 1893 HeapObject.__init__(self, heap, None, None) 1894 self.instance_type = instance_type 1895 self.known_name = known_name 1896 1897 def __str__(self): 1898 return "<%s>" % self.known_name 1899 1900 1901COMMENT_RE = re.compile(r"^C (0x[0-9a-fA-F]+) (.*)$") 1902PAGEADDRESS_RE = re.compile( 1903 r"^P (mappage|oldpage) (0x[0-9a-fA-F]+)$") 1904 1905 1906class InspectionInfo(object): 1907 def __init__(self, minidump_name, reader): 1908 self.comment_file = minidump_name + ".comments" 1909 self.address_comments = {} 1910 self.page_address = {} 1911 if os.path.exists(self.comment_file): 1912 with open(self.comment_file, "r") as f: 1913 lines = f.readlines() 1914 f.close() 1915 1916 for l in lines: 1917 m = COMMENT_RE.match(l) 1918 if m: 1919 self.address_comments[int(m.group(1), 0)] = m.group(2) 1920 m = PAGEADDRESS_RE.match(l) 1921 if m: 1922 self.page_address[m.group(1)] = int(m.group(2), 0) 1923 self.reader = reader 1924 self.styles = {} 1925 self.color_addresses() 1926 return 1927 1928 def get_page_address(self, page_kind): 1929 return self.page_address.get(page_kind, 0) 1930 1931 def save_page_address(self, page_kind, address): 1932 with open(self.comment_file, "a") as f: 1933 f.write("P %s 0x%x\n" % (page_kind, address)) 1934 f.close() 1935 1936 def color_addresses(self): 1937 # Color all stack addresses. 1938 exception_thread = self.reader.thread_map[self.reader.exception.thread_id] 1939 stack_top = self.reader.ExceptionSP() 1940 stack_bottom = exception_thread.stack.start + \ 1941 exception_thread.stack.memory.data_size 1942 frame_pointer = self.reader.ExceptionFP() 1943 self.styles[frame_pointer] = "frame" 1944 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()): 1945 # stack address 1946 self.styles[slot] = "sa" 1947 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()): 1948 maybe_address = self.reader.ReadUIntPtr(slot) 1949 # stack value 1950 self.styles[maybe_address] = "sv" 1951 if slot == frame_pointer: 1952 self.styles[slot] = "frame" 1953 frame_pointer = maybe_address 1954 self.styles[self.reader.ExceptionIP()] = "pc" 1955 1956 def get_style_class(self, address): 1957 return self.styles.get(address, None) 1958 1959 def get_style_class_string(self, address): 1960 style = self.get_style_class(address) 1961 if style != None: 1962 return " class=%s " % style 1963 else: 1964 return "" 1965 1966 def set_comment(self, address, comment): 1967 self.address_comments[address] = comment 1968 with open(self.comment_file, "a") as f: 1969 f.write("C 0x%x %s\n" % (address, comment)) 1970 f.close() 1971 1972 def get_comment(self, address): 1973 return self.address_comments.get(address, "") 1974 1975 1976class InspectionPadawan(object): 1977 """The padawan can improve annotations by sensing well-known objects.""" 1978 def __init__(self, reader, heap): 1979 self.reader = reader 1980 self.heap = heap 1981 self.known_first_map_page = 0 1982 self.known_first_old_page = 0 1983 self.context = None 1984 1985 def __getattr__(self, name): 1986 """An InspectionPadawan can be used instead of V8Heap, even though 1987 it does not inherit from V8Heap (aka. mixin).""" 1988 return getattr(self.heap, name) 1989 1990 def GetPageOffset(self, tagged_address): 1991 return tagged_address & self.heap.PageAlignmentMask() 1992 1993 def IsInKnownMapSpace(self, tagged_address): 1994 page_address = tagged_address & ~self.heap.PageAlignmentMask() 1995 return page_address == self.known_first_map_page 1996 1997 def IsInKnownOldSpace(self, tagged_address): 1998 page_address = tagged_address & ~self.heap.PageAlignmentMask() 1999 return page_address == self.known_first_old_page 2000 2001 def ContainingKnownOldSpaceName(self, tagged_address): 2002 page_address = tagged_address & ~self.heap.PageAlignmentMask() 2003 if page_address == self.known_first_old_page: return "OLD_SPACE" 2004 return None 2005 2006 def FrameMarkerName(self, value): 2007 # The frame marker is Smi-tagged but not Smi encoded and 0 is not a valid 2008 # frame type. 2009 value = (value >> 1) - 1 2010 if 0 <= value < len(FRAME_MARKERS): 2011 return "Possibly %s frame marker" % FRAME_MARKERS[value] 2012 return None 2013 2014 def IsFrameMarker(self, slot, address): 2015 if not slot: return False 2016 # Frame markers only occur directly after a frame pointer and only on the 2017 # stack. 2018 if not self.reader.IsExceptionStackAddress(slot): return False 2019 next_slot = slot + self.reader.PointerSize() 2020 if not self.reader.IsValidAddress(next_slot): return False 2021 next_address = self.reader.ReadUIntPtr(next_slot) 2022 return self.reader.IsExceptionStackAddress(next_address) 2023 2024 def FormatSmi(self, address): 2025 value = self.heap.SmiUntag(address) 2026 # On 32-bit systems almost everything looks like a Smi. 2027 if not self.reader.Is64() or value == 0: return None 2028 return "Smi(%d)" % value 2029 2030 def SenseObject(self, address, slot=None): 2031 if self.IsFrameMarker(slot, address): 2032 return self.FrameMarkerName(address) 2033 if self.heap.IsSmi(address): 2034 return self.FormatSmi(address) 2035 if not self.heap.IsTaggedAddress(address): return None 2036 tagged_address = address 2037 if self.IsInKnownOldSpace(tagged_address): 2038 offset = self.GetPageOffset(tagged_address) 2039 lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset) 2040 known_obj_name = KNOWN_OBJECTS.get(lookup_key) 2041 if known_obj_name: 2042 return KnownObject(self, known_obj_name) 2043 if self.IsInKnownMapSpace(tagged_address): 2044 known_map = self.SenseMap(tagged_address) 2045 if known_map: 2046 return known_map 2047 found_obj = self.heap.FindObject(tagged_address) 2048 if found_obj: return found_obj 2049 address = tagged_address - 1 2050 if self.reader.IsValidAddress(address): 2051 map_tagged_address = self.reader.ReadUIntPtr(address) 2052 map = self.SenseMap(map_tagged_address) 2053 if map is None: return None 2054 instance_type_name = INSTANCE_TYPES.get(map.instance_type) 2055 if instance_type_name is None: return None 2056 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject) 2057 return cls(self, map, address) 2058 return None 2059 2060 def SenseMap(self, tagged_address): 2061 if self.IsInKnownMapSpace(tagged_address): 2062 offset = self.GetPageOffset(tagged_address) 2063 lookup_key = ("MAP_SPACE", offset) 2064 known_map_info = KNOWN_MAPS.get(lookup_key) 2065 if known_map_info: 2066 known_map_type, known_map_name = known_map_info 2067 return KnownMap(self, known_map_name, known_map_type) 2068 found_map = self.heap.FindMap(tagged_address) 2069 if found_map: return found_map 2070 return None 2071 2072 def FindObjectOrSmi(self, tagged_address): 2073 """When used as a mixin in place of V8Heap.""" 2074 found_obj = self.SenseObject(tagged_address) 2075 if found_obj: return found_obj 2076 if self.IsSmi(tagged_address): 2077 return self.FormatSmi(tagged_address) 2078 else: 2079 return "Unknown(%s)" % self.reader.FormatIntPtr(tagged_address) 2080 2081 def FindObject(self, tagged_address): 2082 """When used as a mixin in place of V8Heap.""" 2083 raise NotImplementedError 2084 2085 def FindMap(self, tagged_address): 2086 """When used as a mixin in place of V8Heap.""" 2087 raise NotImplementedError 2088 2089 def PrintKnowledge(self): 2090 print " known_first_map_page = %s\n"\ 2091 " known_first_old_page = %s" % ( 2092 self.reader.FormatIntPtr(self.known_first_map_page), 2093 self.reader.FormatIntPtr(self.known_first_old_page)) 2094 2095 def FindFirstAsciiString(self, start, end=None, min_length=32): 2096 """ Walk the memory until we find a large string """ 2097 if not end: end = start + 64 2098 for slot in xrange(start, end): 2099 if not self.reader.IsValidAddress(slot): break 2100 message = self.reader.ReadAsciiString(slot) 2101 if len(message) > min_length: 2102 return (slot, message) 2103 return (None,None) 2104 2105 def PrintStackTraceMessage(self, start=None, print_message=True): 2106 """ 2107 Try to print a possible message from PushStackTraceAndDie. 2108 Returns the first address where the normal stack starts again. 2109 """ 2110 # Only look at the first 1k words on the stack 2111 ptr_size = self.reader.PointerSize() 2112 if start is None: start = self.reader.ExceptionSP() 2113 if not self.reader.IsValidAddress(start): return start 2114 end = start + ptr_size * 1024 * 4 2115 magic1 = None 2116 for slot in xrange(start, end, ptr_size): 2117 if not self.reader.IsValidAddress(slot + ptr_size): break 2118 magic1 = self.reader.ReadUIntPtr(slot) 2119 magic2 = self.reader.ReadUIntPtr(slot + ptr_size) 2120 pair = (magic1 & 0xFFFFFFFF, magic2 & 0xFFFFFFFF) 2121 if pair in MAGIC_MARKER_PAIRS: 2122 return self.TryExtractOldStyleStackTrace(slot, start, end, 2123 print_message) 2124 if pair[0] == STACK_TRACE_MARKER: 2125 return self.TryExtractStackTrace(slot, start, end, print_message) 2126 elif pair[0] == ERROR_MESSAGE_MARKER: 2127 return self.TryExtractErrorMessage(slot, start, end, print_message) 2128 # Simple fallback in case not stack trace object was found 2129 return self.TryExtractOldStyleStackTrace(0, start, end, 2130 print_message) 2131 2132 def TryExtractStackTrace(self, slot, start, end, print_message): 2133 ptr_size = self.reader.PointerSize() 2134 assert self.reader.ReadUIntPtr(slot) & 0xFFFFFFFF == STACK_TRACE_MARKER 2135 end_marker = STACK_TRACE_MARKER + 1; 2136 header_size = 10 2137 # Look for the end marker after the fields and the message buffer. 2138 end_search = start + (32 * 1024) + (header_size * ptr_size); 2139 end_slot = self.FindPtr(end_marker, end_search, end_search + ptr_size * 512) 2140 if not end_slot: return start 2141 print "Stack Message (start=%s):" % self.heap.FormatIntPtr(slot) 2142 slot += ptr_size 2143 for name in ("isolate","ptr1", "ptr2", "ptr3", "ptr4", "codeObject1", 2144 "codeObject2", "codeObject3", "codeObject4"): 2145 value = self.reader.ReadUIntPtr(slot) 2146 print " %s: %s" % (name.rjust(14), self.heap.FormatIntPtr(value)) 2147 slot += ptr_size 2148 print " message start: %s" % self.heap.FormatIntPtr(slot) 2149 stack_start = end_slot + ptr_size 2150 print " stack_start: %s" % self.heap.FormatIntPtr(stack_start) 2151 (message_start, message) = self.FindFirstAsciiString(slot) 2152 self.FormatStackTrace(message, print_message) 2153 return stack_start 2154 2155 def FindPtr(self, expected_value, start, end): 2156 ptr_size = self.reader.PointerSize() 2157 for slot in xrange(start, end, ptr_size): 2158 if not self.reader.IsValidAddress(slot): return None 2159 value = self.reader.ReadUIntPtr(slot) 2160 if value == expected_value: return slot 2161 return None 2162 2163 def TryExtractErrorMessage(self, slot, start, end, print_message): 2164 ptr_size = self.reader.PointerSize() 2165 end_marker = ERROR_MESSAGE_MARKER + 1; 2166 header_size = 1 2167 end_search = start + 1024 + (header_size * ptr_size); 2168 end_slot = self.FindPtr(end_marker, end_search, end_search + ptr_size * 512) 2169 if not end_slot: return start 2170 print "Error Message (start=%s):" % self.heap.FormatIntPtr(slot) 2171 slot += ptr_size 2172 (message_start, message) = self.FindFirstAsciiString(slot) 2173 self.FormatStackTrace(message, print_message) 2174 stack_start = end_slot + ptr_size 2175 return stack_start 2176 2177 def TryExtractOldStyleStackTrace(self, message_slot, start, end, 2178 print_message): 2179 ptr_size = self.reader.PointerSize() 2180 if message_slot == 0: 2181 """ 2182 On Mac we don't always get proper magic markers, so just try printing 2183 the first long ascii string found on the stack. 2184 """ 2185 magic1 = None 2186 magic2 = None 2187 message_start, message = self.FindFirstAsciiString(start, end, 128) 2188 if message_start is None: return start 2189 else: 2190 message_start = self.reader.ReadUIntPtr(message_slot + ptr_size * 4) 2191 message = self.reader.ReadAsciiString(message_start) 2192 stack_start = message_start + len(message) + 1 2193 # Make sure the address is word aligned 2194 stack_start = stack_start - (stack_start % ptr_size) 2195 if magic1 is None: 2196 print "Stack Message:" 2197 print " message start: %s" % self.heap.FormatIntPtr(message_start) 2198 print " stack_start: %s" % self.heap.FormatIntPtr(stack_start ) 2199 else: 2200 ptr1 = self.reader.ReadUIntPtr(slot + ptr_size * 2) 2201 ptr2 = self.reader.ReadUIntPtr(slot + ptr_size * 3) 2202 print "Stack Message:" 2203 print " magic1: %s" % self.heap.FormatIntPtr(magic1) 2204 print " magic2: %s" % self.heap.FormatIntPtr(magic2) 2205 print " ptr1: %s" % self.heap.FormatIntPtr(ptr1) 2206 print " ptr2: %s" % self.heap.FormatIntPtr(ptr2) 2207 print " message start: %s" % self.heap.FormatIntPtr(message_start) 2208 print " stack_start: %s" % self.heap.FormatIntPtr(stack_start ) 2209 print "" 2210 self.FormatStackTrace(message, print_message) 2211 return stack_start 2212 2213 def FormatStackTrace(self, message, print_message): 2214 if not print_message: 2215 print " Use `dsa` to print the message with annotated addresses." 2216 print "" 2217 return 2218 ptr_size = self.reader.PointerSize() 2219 # Annotate all addresses in the dumped message 2220 prog = re.compile("[0-9a-fA-F]{%s}" % ptr_size*2) 2221 addresses = list(set(prog.findall(message))) 2222 for i in range(len(addresses)): 2223 address_org = addresses[i] 2224 address = self.heap.FormatIntPtr(int(address_org, 16)) 2225 if address_org != address: 2226 message = message.replace(address_org, address) 2227 print "Message:" 2228 print "="*80 2229 print message 2230 print "="*80 2231 print "" 2232 2233 2234 def TryInferFramePointer(self, slot, address): 2235 """ Assume we have a framepointer if we find 4 consecutive links """ 2236 for i in range(0, 4): 2237 if not self.reader.IsExceptionStackAddress(address): return 0 2238 next_address = self.reader.ReadUIntPtr(address) 2239 if next_address == address: return 0 2240 address = next_address 2241 return slot 2242 2243 def TryInferContext(self, address): 2244 if self.context: return 2245 ptr_size = self.reader.PointerSize() 2246 possible_context = dict() 2247 count = 0 2248 while self.reader.IsExceptionStackAddress(address): 2249 prev_addr = self.reader.ReadUIntPtr(address-ptr_size) 2250 if self.heap.IsTaggedObjectAddress(prev_addr): 2251 if prev_addr in possible_context: 2252 possible_context[prev_addr] += 1 2253 else: 2254 possible_context[prev_addr] = 1 2255 address = self.reader.ReadUIntPtr(address) 2256 count += 1 2257 if count <= 5 or len(possible_context) == 0: return 2258 # Find entry with highest count 2259 possible_context = possible_context.items() 2260 possible_context.sort(key=lambda pair: pair[1]) 2261 address,count = possible_context[-1] 2262 if count <= 4: return 2263 self.context = address 2264 2265 def InterpretMemory(self, start, end): 2266 # On 64 bit we omit frame pointers, so we have to do some more guesswork. 2267 frame_pointer = 0 2268 if not self.reader.Is64(): 2269 frame_pointer = self.reader.ExceptionFP() 2270 # Follow the framepointer into the address range 2271 while frame_pointer and frame_pointer < start: 2272 frame_pointer = self.reader.ReadUIntPtr(frame_pointer) 2273 if not self.reader.IsExceptionStackAddress(frame_pointer) or \ 2274 not frame_pointer: 2275 frame_pointer = 0 2276 break 2277 in_oom_dump_area = False 2278 is_stack = self.reader.IsExceptionStackAddress(start) 2279 free_space_end = 0 2280 ptr_size = self.reader.PointerSize() 2281 2282 for slot in xrange(start, end, ptr_size): 2283 if not self.reader.IsValidAddress(slot): 2284 print "%s: Address is not contained within the minidump!" % slot 2285 return 2286 maybe_address = self.reader.ReadUIntPtr(slot) 2287 address_info = [] 2288 # Mark continuous free space objects 2289 if slot == free_space_end: 2290 address_info.append("+") 2291 elif slot <= free_space_end: 2292 address_info.append("|") 2293 else: 2294 free_space_end = 0 2295 2296 heap_object = self.SenseObject(maybe_address, slot) 2297 if heap_object: 2298 # Detect Free-space ranges 2299 if isinstance(heap_object, KnownMap) and \ 2300 heap_object.known_name == "FreeSpaceMap": 2301 # The free-space length is is stored as a Smi in the next slot. 2302 length = self.reader.ReadUIntPtr(slot + ptr_size) 2303 if self.heap.IsSmi(length): 2304 length = self.heap.SmiUntag(length) 2305 free_space_end = slot + length - ptr_size 2306 address_info.append(str(heap_object)) 2307 relative_offset = self.heap.RelativeOffset(slot, maybe_address) 2308 if relative_offset: 2309 address_info.append(relative_offset) 2310 if maybe_address == self.context: 2311 address_info.append("CONTEXT") 2312 2313 maybe_address_contents = None 2314 if is_stack: 2315 if self.reader.IsExceptionStackAddress(maybe_address): 2316 maybe_address_contents = \ 2317 self.reader.ReadUIntPtr(maybe_address) & 0xFFFFFFFF 2318 if maybe_address_contents == 0xdecade00: 2319 in_oom_dump_area = True 2320 if frame_pointer == 0: 2321 frame_pointer = self.TryInferFramePointer(slot, maybe_address) 2322 if frame_pointer != 0: 2323 self.TryInferContext(slot) 2324 maybe_symbol = self.reader.FindSymbol(maybe_address) 2325 if in_oom_dump_area: 2326 if maybe_address_contents == 0xdecade00: 2327 address_info = ["<==== HeapStats start marker"] 2328 elif maybe_address_contents == 0xdecade01: 2329 address_info = ["<==== HeapStats end marker"] 2330 elif maybe_address_contents is not None: 2331 address_info = [" %d (%d Mbytes)" % (maybe_address_contents, 2332 maybe_address_contents >> 20)] 2333 if slot == frame_pointer: 2334 if not self.reader.IsExceptionStackAddress(maybe_address): 2335 address_info.append("<==== BAD frame pointer") 2336 frame_pointer = 0 2337 else: 2338 address_info.append("<==== Frame pointer") 2339 frame_pointer = maybe_address 2340 address_type_marker = self.heap.AddressTypeMarker(maybe_address) 2341 string_value = self.reader.ReadAsciiPtr(slot) 2342 print "%s: %s %s %s %s" % (self.reader.FormatIntPtr(slot), 2343 self.reader.FormatIntPtr(maybe_address), 2344 address_type_marker, 2345 string_value, 2346 ' | '.join(address_info)) 2347 if maybe_address_contents == 0xdecade01: 2348 in_oom_dump_area = False 2349 heap_object = self.heap.FindObject(maybe_address) 2350 if heap_object: 2351 heap_object.Print(Printer()) 2352 print "" 2353 2354WEB_HEADER = """ 2355<!DOCTYPE html> 2356<html> 2357<head> 2358<meta content="text/html; charset=utf-8" http-equiv="content-type"> 2359<style media="screen" type="text/css"> 2360 2361.code { 2362 font-family: monospace; 2363} 2364 2365.dmptable { 2366 border-collapse : collapse; 2367 border-spacing : 0px; 2368 table-layout: fixed; 2369} 2370 2371.codedump { 2372 border-collapse : collapse; 2373 border-spacing : 0px; 2374 table-layout: fixed; 2375} 2376 2377.addrcomments { 2378 border : 0px; 2379} 2380 2381.register { 2382 padding-right : 1em; 2383} 2384 2385.header { 2386 clear : both; 2387} 2388 2389.header .navigation { 2390 float : left; 2391} 2392 2393.header .dumpname { 2394 float : right; 2395} 2396 2397tr.highlight-line { 2398 background-color : yellow; 2399} 2400 2401.highlight { 2402 background-color : magenta; 2403} 2404 2405tr.inexact-highlight-line { 2406 background-color : pink; 2407} 2408 2409input { 2410 background-color: inherit; 2411 border: 1px solid LightGray; 2412} 2413 2414.dumpcomments { 2415 border : 1px solid LightGray; 2416 width : 32em; 2417} 2418 2419.regions td { 2420 padding:0 15px 0 15px; 2421} 2422 2423.stackframe td { 2424 background-color : cyan; 2425} 2426 2427.stackaddress, .sa { 2428 background-color : LightGray; 2429} 2430 2431.stackval, .sv { 2432 background-color : LightCyan; 2433} 2434 2435.frame { 2436 background-color : cyan; 2437} 2438 2439.commentinput, .ci { 2440 width : 20em; 2441} 2442 2443/* a.nodump */ 2444a.nd:visited { 2445 color : black; 2446 text-decoration : none; 2447} 2448 2449a.nd:link { 2450 color : black; 2451 text-decoration : none; 2452} 2453 2454a:visited { 2455 color : blueviolet; 2456} 2457 2458a:link { 2459 color : blue; 2460} 2461 2462.disasmcomment { 2463 color : DarkGreen; 2464} 2465 2466</style> 2467 2468<script type="application/javascript"> 2469 2470var address_str = "address-"; 2471var address_len = address_str.length; 2472 2473function comment() { 2474 var s = event.srcElement.id; 2475 var index = s.indexOf(address_str); 2476 if (index >= 0) { 2477 send_comment(s.substring(index + address_len), event.srcElement.value); 2478 } 2479} 2480var c = comment; 2481 2482function send_comment(address, comment) { 2483 xmlhttp = new XMLHttpRequest(); 2484 address = encodeURIComponent(address) 2485 comment = encodeURIComponent(comment) 2486 xmlhttp.open("GET", 2487 "setcomment?%(query_dump)s&address=" + address + 2488 "&comment=" + comment, true); 2489 xmlhttp.send(); 2490} 2491 2492var dump_str = "dump-"; 2493var dump_len = dump_str.length; 2494 2495function dump_comment() { 2496 var s = event.srcElement.id; 2497 var index = s.indexOf(dump_str); 2498 if (index >= 0) { 2499 send_dump_desc(s.substring(index + dump_len), event.srcElement.value); 2500 } 2501} 2502 2503function send_dump_desc(name, desc) { 2504 xmlhttp = new XMLHttpRequest(); 2505 name = encodeURIComponent(name) 2506 desc = encodeURIComponent(desc) 2507 xmlhttp.open("GET", 2508 "setdumpdesc?dump=" + name + 2509 "&description=" + desc, true); 2510 xmlhttp.send(); 2511} 2512 2513function onpage(kind, address) { 2514 xmlhttp = new XMLHttpRequest(); 2515 kind = encodeURIComponent(kind) 2516 address = encodeURIComponent(address) 2517 xmlhttp.onreadystatechange = function() { 2518 if (xmlhttp.readyState==4 && xmlhttp.status==200) { 2519 location.reload(true) 2520 } 2521 }; 2522 xmlhttp.open("GET", 2523 "setpageaddress?%(query_dump)s&kind=" + kind + 2524 "&address=" + address); 2525 xmlhttp.send(); 2526} 2527 2528</script> 2529 2530<title>Dump %(dump_name)s</title> 2531</head> 2532 2533<body> 2534 <div class="header"> 2535 <form class="navigation" action="search.html"> 2536 <a href="summary.html?%(query_dump)s">Context info</a> 2537 <a href="info.html?%(query_dump)s">Dump info</a> 2538 <a href="modules.html?%(query_dump)s">Modules</a> 2539 2540 <input type="search" name="val"> 2541 <input type="submit" name="search" value="Search"> 2542 <input type="hidden" name="dump" value="%(dump_name)s"> 2543 </form> 2544 <form class="navigation" action="disasm.html#highlight"> 2545 2546 2547 2548 <input type="search" name="val"> 2549 <input type="submit" name="disasm" value="Disasm"> 2550 2551 2552 2553 <a href="dumps.html">Dumps...</a> 2554 </form> 2555 </div> 2556 <br> 2557 <hr> 2558""" 2559 2560 2561WEB_FOOTER = """ 2562</body> 2563</html> 2564""" 2565 2566 2567class WebParameterError(Exception): 2568 def __init__(self, message): 2569 Exception.__init__(self, message) 2570 2571 2572class InspectionWebHandler(BaseHTTPServer.BaseHTTPRequestHandler): 2573 def formatter(self, query_components): 2574 name = query_components.get("dump", [None])[0] 2575 return self.server.get_dump_formatter(name) 2576 2577 def send_success_html_headers(self): 2578 self.send_response(200) 2579 self.send_header("Cache-Control", "no-cache, no-store, must-revalidate") 2580 self.send_header("Pragma", "no-cache") 2581 self.send_header("Expires", "0") 2582 self.send_header('Content-type','text/html') 2583 self.end_headers() 2584 return 2585 2586 def do_GET(self): 2587 try: 2588 parsedurl = urlparse.urlparse(self.path) 2589 query_components = urlparse.parse_qs(parsedurl.query) 2590 if parsedurl.path == "/dumps.html": 2591 self.send_success_html_headers() 2592 out_buffer = StringIO.StringIO() 2593 self.server.output_dumps(out_buffer) 2594 self.wfile.write(out_buffer.getvalue()) 2595 elif parsedurl.path == "/summary.html": 2596 self.send_success_html_headers() 2597 out_buffer = StringIO.StringIO() 2598 self.formatter(query_components).output_summary(out_buffer) 2599 self.wfile.write(out_buffer.getvalue()) 2600 elif parsedurl.path == "/info.html": 2601 self.send_success_html_headers() 2602 out_buffer = StringIO.StringIO() 2603 self.formatter(query_components).output_info(out_buffer) 2604 self.wfile.write(out_buffer.getvalue()) 2605 elif parsedurl.path == "/modules.html": 2606 self.send_success_html_headers() 2607 out_buffer = StringIO.StringIO() 2608 self.formatter(query_components).output_modules(out_buffer) 2609 self.wfile.write(out_buffer.getvalue()) 2610 elif parsedurl.path == "/search.html" or parsedurl.path == "/s": 2611 address = query_components.get("val", []) 2612 if len(address) != 1: 2613 self.send_error(404, "Invalid params") 2614 return 2615 self.send_success_html_headers() 2616 out_buffer = StringIO.StringIO() 2617 self.formatter(query_components).output_search_res( 2618 out_buffer, address[0]) 2619 self.wfile.write(out_buffer.getvalue()) 2620 elif parsedurl.path == "/disasm.html": 2621 address = query_components.get("val", []) 2622 exact = query_components.get("exact", ["on"]) 2623 if len(address) != 1: 2624 self.send_error(404, "Invalid params") 2625 return 2626 self.send_success_html_headers() 2627 out_buffer = StringIO.StringIO() 2628 self.formatter(query_components).output_disasm( 2629 out_buffer, address[0], exact[0]) 2630 self.wfile.write(out_buffer.getvalue()) 2631 elif parsedurl.path == "/data.html": 2632 address = query_components.get("val", []) 2633 datakind = query_components.get("type", ["address"]) 2634 if len(address) == 1 and len(datakind) == 1: 2635 self.send_success_html_headers() 2636 out_buffer = StringIO.StringIO() 2637 self.formatter(query_components).output_data( 2638 out_buffer, address[0], datakind[0]) 2639 self.wfile.write(out_buffer.getvalue()) 2640 else: 2641 self.send_error(404,'Invalid params') 2642 elif parsedurl.path == "/setdumpdesc": 2643 name = query_components.get("dump", [""]) 2644 description = query_components.get("description", [""]) 2645 if len(name) == 1 and len(description) == 1: 2646 name = name[0] 2647 description = description[0] 2648 if self.server.set_dump_desc(name, description): 2649 self.send_success_html_headers() 2650 self.wfile.write("OK") 2651 return 2652 self.send_error(404,'Invalid params') 2653 elif parsedurl.path == "/setcomment": 2654 address = query_components.get("address", []) 2655 comment = query_components.get("comment", [""]) 2656 if len(address) == 1 and len(comment) == 1: 2657 address = address[0] 2658 comment = comment[0] 2659 self.formatter(query_components).set_comment(address, comment) 2660 self.send_success_html_headers() 2661 self.wfile.write("OK") 2662 else: 2663 self.send_error(404,'Invalid params') 2664 elif parsedurl.path == "/setpageaddress": 2665 kind = query_components.get("kind", []) 2666 address = query_components.get("address", [""]) 2667 if len(kind) == 1 and len(address) == 1: 2668 kind = kind[0] 2669 address = address[0] 2670 self.formatter(query_components).set_page_address(kind, address) 2671 self.send_success_html_headers() 2672 self.wfile.write("OK") 2673 else: 2674 self.send_error(404,'Invalid params') 2675 else: 2676 self.send_error(404,'File Not Found: %s' % self.path) 2677 2678 except IOError: 2679 self.send_error(404,'File Not Found: %s' % self.path) 2680 2681 except WebParameterError as e: 2682 self.send_error(404, 'Web parameter error: %s' % e.message) 2683 2684 2685HTML_REG_FORMAT = "<span class=\"register\"><b>%s</b>: %s</span><br/>\n" 2686 2687 2688class InspectionWebFormatter(object): 2689 CONTEXT_FULL = 0 2690 CONTEXT_SHORT = 1 2691 2692 def __init__(self, switches, minidump_name, http_server): 2693 self.dumpfilename = os.path.split(minidump_name)[1] 2694 self.encfilename = urllib.urlencode({ 'dump' : self.dumpfilename }) 2695 self.reader = MinidumpReader(switches, minidump_name) 2696 self.server = http_server 2697 2698 # Set up the heap 2699 exception_thread = self.reader.thread_map[self.reader.exception.thread_id] 2700 stack_top = self.reader.ExceptionSP() 2701 stack_bottom = exception_thread.stack.start + \ 2702 exception_thread.stack.memory.data_size 2703 stack_map = {self.reader.ExceptionIP(): -1} 2704 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()): 2705 maybe_address = self.reader.ReadUIntPtr(slot) 2706 if not maybe_address in stack_map: 2707 stack_map[maybe_address] = slot 2708 self.heap = V8Heap(self.reader, stack_map) 2709 2710 self.padawan = InspectionPadawan(self.reader, self.heap) 2711 self.comments = InspectionInfo(minidump_name, self.reader) 2712 self.padawan.known_first_old_page = ( 2713 self.comments.get_page_address("oldpage")) 2714 self.padawan.known_first_map_page = ( 2715 self.comments.get_page_address("mappage")) 2716 2717 def set_comment(self, straddress, comment): 2718 try: 2719 address = int(straddress, 0) 2720 self.comments.set_comment(address, comment) 2721 except ValueError: 2722 print "Invalid address" 2723 2724 def set_page_address(self, kind, straddress): 2725 try: 2726 address = int(straddress, 0) 2727 if kind == "oldpage": 2728 self.padawan.known_first_old_page = address 2729 elif kind == "mappage": 2730 self.padawan.known_first_map_page = address 2731 self.comments.save_page_address(kind, address) 2732 except ValueError: 2733 print "Invalid address" 2734 2735 def td_from_address(self, f, address): 2736 f.write("<td %s>" % self.comments.get_style_class_string(address)) 2737 2738 def format_address(self, maybeaddress, straddress = None): 2739 if maybeaddress is None: 2740 return "not in dump" 2741 else: 2742 if straddress is None: 2743 straddress = "0x" + self.reader.FormatIntPtr(maybeaddress) 2744 style_class = "" 2745 if not self.reader.IsValidAddress(maybeaddress): 2746 style_class = "class=nd" 2747 return ("<a %s href=s?%s&val=%s>%s</a>" % 2748 (style_class, self.encfilename, straddress, straddress)) 2749 2750 def output_header(self, f): 2751 f.write(WEB_HEADER % 2752 { "query_dump" : self.encfilename, 2753 "dump_name" : cgi.escape(self.dumpfilename) }) 2754 2755 def output_footer(self, f): 2756 f.write(WEB_FOOTER) 2757 2758 MAX_CONTEXT_STACK = 2048 2759 2760 def output_summary(self, f): 2761 self.output_header(f) 2762 f.write('<div class="code">') 2763 self.output_context(f, InspectionWebFormatter.CONTEXT_SHORT) 2764 self.output_disasm_pc(f) 2765 2766 # Output stack 2767 exception_thread = self.reader.thread_map[self.reader.exception.thread_id] 2768 stack_top = self.reader.ExceptionSP() 2769 stack_bottom = min(exception_thread.stack.start + \ 2770 exception_thread.stack.memory.data_size, 2771 stack_top + self.MAX_CONTEXT_STACK) 2772 self.output_words(f, stack_top - 16, stack_bottom, stack_top, "Stack") 2773 2774 f.write('</div>') 2775 self.output_footer(f) 2776 return 2777 2778 def output_info(self, f): 2779 self.output_header(f) 2780 f.write("<h3>Dump info</h3>") 2781 f.write("Description: ") 2782 self.server.output_dump_desc_field(f, self.dumpfilename) 2783 f.write("<br>") 2784 f.write("Filename: ") 2785 f.write("<span class=\"code\">%s</span><br>" % (self.dumpfilename)) 2786 dt = datetime.datetime.fromtimestamp(self.reader.header.time_date_stampt) 2787 f.write("Timestamp: %s<br>" % dt.strftime('%Y-%m-%d %H:%M:%S')) 2788 self.output_context(f, InspectionWebFormatter.CONTEXT_FULL) 2789 self.output_address_ranges(f) 2790 self.output_footer(f) 2791 return 2792 2793 def output_address_ranges(self, f): 2794 regions = {} 2795 def print_region(_reader, start, size, _location): 2796 regions[start] = size 2797 self.reader.ForEachMemoryRegion(print_region) 2798 f.write("<h3>Available memory regions</h3>") 2799 f.write('<div class="code">') 2800 f.write("<table class=\"regions\">") 2801 f.write("<thead><tr>") 2802 f.write("<th>Start address</th>") 2803 f.write("<th>End address</th>") 2804 f.write("<th>Number of bytes</th>") 2805 f.write("</tr></thead>") 2806 for start in sorted(regions): 2807 size = regions[start] 2808 f.write("<tr>") 2809 f.write("<td>%s</td>" % self.format_address(start)) 2810 f.write("<td> %s</td>" % self.format_address(start + size)) 2811 f.write("<td> %d</td>" % size) 2812 f.write("</tr>") 2813 f.write("</table>") 2814 f.write('</div>') 2815 return 2816 2817 def output_module_details(self, f, module): 2818 f.write("<b>%s</b>" % GetModuleName(self.reader, module)) 2819 file_version = GetVersionString(module.version_info.dwFileVersionMS, 2820 module.version_info.dwFileVersionLS) 2821 product_version = GetVersionString(module.version_info.dwProductVersionMS, 2822 module.version_info.dwProductVersionLS) 2823 f.write("<br> ") 2824 f.write("base: %s" % self.reader.FormatIntPtr(module.base_of_image)) 2825 f.write("<br> ") 2826 f.write(" end: %s" % self.reader.FormatIntPtr(module.base_of_image + 2827 module.size_of_image)) 2828 f.write("<br> ") 2829 f.write(" file version: %s" % file_version) 2830 f.write("<br> ") 2831 f.write(" product version: %s" % product_version) 2832 f.write("<br> ") 2833 time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp) 2834 f.write(" timestamp: %s" % time_date_stamp) 2835 f.write("<br>"); 2836 2837 def output_modules(self, f): 2838 self.output_header(f) 2839 f.write('<div class="code">') 2840 for module in self.reader.module_list.modules: 2841 self.output_module_details(f, module) 2842 f.write("</div>") 2843 self.output_footer(f) 2844 return 2845 2846 def output_context(self, f, details): 2847 exception_thread = self.reader.thread_map[self.reader.exception.thread_id] 2848 f.write("<h3>Exception context</h3>") 2849 f.write('<div class="code">') 2850 f.write("Thread id: %d" % exception_thread.id) 2851 f.write(" Exception code: %08X<br/>" % 2852 self.reader.exception.exception.code) 2853 if details == InspectionWebFormatter.CONTEXT_FULL: 2854 if self.reader.exception.exception.parameter_count > 0: 2855 f.write(" Exception parameters: ") 2856 for i in xrange(0, self.reader.exception.exception.parameter_count): 2857 f.write("%08x" % self.reader.exception.exception.information[i]) 2858 f.write("<br><br>") 2859 2860 for r in CONTEXT_FOR_ARCH[self.reader.arch]: 2861 f.write(HTML_REG_FORMAT % 2862 (r, self.format_address(self.reader.Register(r)))) 2863 # TODO(vitalyr): decode eflags. 2864 if self.reader.arch in [MD_CPU_ARCHITECTURE_ARM, MD_CPU_ARCHITECTURE_ARM64]: 2865 f.write("<b>cpsr</b>: %s" % bin(self.reader.exception_context.cpsr)[2:]) 2866 else: 2867 f.write("<b>eflags</b>: %s" % 2868 bin(self.reader.exception_context.eflags)[2:]) 2869 f.write('</div>') 2870 return 2871 2872 def align_down(self, a, size): 2873 alignment_correction = a % size 2874 return a - alignment_correction 2875 2876 def align_up(self, a, size): 2877 alignment_correction = (size - 1) - ((a + size - 1) % size) 2878 return a + alignment_correction 2879 2880 def format_object(self, address): 2881 heap_object = self.padawan.SenseObject(address) 2882 return cgi.escape(str(heap_object or "")) 2883 2884 def output_data(self, f, straddress, datakind): 2885 try: 2886 self.output_header(f) 2887 address = int(straddress, 0) 2888 if not self.reader.IsValidAddress(address): 2889 f.write("<h3>Address 0x%x not found in the dump.</h3>" % address) 2890 return 2891 region = self.reader.FindRegion(address) 2892 if datakind == "address": 2893 self.output_words(f, region[0], region[0] + region[1], address, "Dump") 2894 elif datakind == "ascii": 2895 self.output_ascii(f, region[0], region[0] + region[1], address) 2896 self.output_footer(f) 2897 2898 except ValueError: 2899 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress) 2900 return 2901 2902 def output_words(self, f, start_address, end_address, 2903 highlight_address, desc): 2904 region = self.reader.FindRegion(highlight_address) 2905 if region is None: 2906 f.write("<h3>Address 0x%x not found in the dump.</h3>" % 2907 (highlight_address)) 2908 return 2909 size = self.heap.PointerSize() 2910 start_address = self.align_down(start_address, size) 2911 low = self.align_down(region[0], size) 2912 high = self.align_up(region[0] + region[1], size) 2913 if start_address < low: 2914 start_address = low 2915 end_address = self.align_up(end_address, size) 2916 if end_address > high: 2917 end_address = high 2918 2919 expand = "" 2920 if start_address != low or end_address != high: 2921 expand = ("(<a href=\"data.html?%s&val=0x%x#highlight\">" 2922 " more..." 2923 " </a>)" % 2924 (self.encfilename, highlight_address)) 2925 2926 f.write("<h3>%s 0x%x - 0x%x, " 2927 "highlighting <a href=\"#highlight\">0x%x</a> %s</h3>" % 2928 (desc, start_address, end_address, highlight_address, expand)) 2929 f.write('<div class="code">') 2930 f.write("<table class=codedump>") 2931 2932 for j in xrange(0, end_address - start_address, size): 2933 slot = start_address + j 2934 heap_object = "" 2935 maybe_address = None 2936 end_region = region[0] + region[1] 2937 if slot < region[0] or slot + size > end_region: 2938 straddress = "0x" 2939 for i in xrange(end_region, slot + size): 2940 straddress += "??" 2941 for i in reversed( 2942 xrange(max(slot, region[0]), min(slot + size, end_region))): 2943 straddress += "%02x" % self.reader.ReadU8(i) 2944 for i in xrange(slot, region[0]): 2945 straddress += "??" 2946 else: 2947 maybe_address = self.reader.ReadUIntPtr(slot) 2948 straddress = self.format_address(maybe_address) 2949 if maybe_address: 2950 heap_object = self.format_object(maybe_address) 2951 2952 address_fmt = "%s </td>" 2953 if slot == highlight_address: 2954 f.write("<tr class=highlight-line>") 2955 address_fmt = "<a id=highlight></a>%s </td>" 2956 elif slot < highlight_address and highlight_address < slot + size: 2957 f.write("<tr class=inexact-highlight-line>") 2958 address_fmt = "<a id=highlight></a>%s </td>" 2959 else: 2960 f.write("<tr>") 2961 2962 f.write("<td>") 2963 self.output_comment_box(f, "da-", slot) 2964 f.write("</td>") 2965 self.td_from_address(f, slot) 2966 f.write(address_fmt % self.format_address(slot)) 2967 self.td_from_address(f, maybe_address) 2968 f.write(": %s </td>" % straddress) 2969 f.write("<td>") 2970 if maybe_address != None: 2971 self.output_comment_box( 2972 f, "sv-" + self.reader.FormatIntPtr(slot), maybe_address) 2973 f.write("</td>") 2974 f.write("<td>%s</td>" % (heap_object or '')) 2975 f.write("</tr>") 2976 f.write("</table>") 2977 f.write("</div>") 2978 return 2979 2980 def output_ascii(self, f, start_address, end_address, highlight_address): 2981 region = self.reader.FindRegion(highlight_address) 2982 if region is None: 2983 f.write("<h3>Address %x not found in the dump.</h3>" % 2984 highlight_address) 2985 return 2986 if start_address < region[0]: 2987 start_address = region[0] 2988 if end_address > region[0] + region[1]: 2989 end_address = region[0] + region[1] 2990 2991 expand = "" 2992 if start_address != region[0] or end_address != region[0] + region[1]: 2993 link = ("data.html?%s&val=0x%x&type=ascii#highlight" % 2994 (self.encfilename, highlight_address)) 2995 expand = "(<a href=\"%s\">more...</a>)" % link 2996 2997 f.write("<h3>ASCII dump 0x%x - 0x%x, highlighting 0x%x %s</h3>" % 2998 (start_address, end_address, highlight_address, expand)) 2999 3000 line_width = 64 3001 3002 f.write('<div class="code">') 3003 3004 start = self.align_down(start_address, line_width) 3005 3006 for i in xrange(end_address - start): 3007 address = start + i 3008 if address % 64 == 0: 3009 if address != start: 3010 f.write("<br>") 3011 f.write("0x%08x: " % address) 3012 if address < start_address: 3013 f.write(" ") 3014 else: 3015 if address == highlight_address: 3016 f.write("<span class=\"highlight\">") 3017 code = self.reader.ReadU8(address) 3018 if code < 127 and code >= 32: 3019 f.write("&#") 3020 f.write(str(code)) 3021 f.write(";") 3022 else: 3023 f.write("·") 3024 if address == highlight_address: 3025 f.write("</span>") 3026 f.write("</div>") 3027 return 3028 3029 def output_disasm(self, f, straddress, strexact): 3030 try: 3031 self.output_header(f) 3032 address = int(straddress, 0) 3033 if not self.reader.IsValidAddress(address): 3034 f.write("<h3>Address 0x%x not found in the dump.</h3>" % address) 3035 return 3036 region = self.reader.FindRegion(address) 3037 self.output_disasm_range( 3038 f, region[0], region[0] + region[1], address, strexact == "on") 3039 self.output_footer(f) 3040 except ValueError: 3041 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress) 3042 return 3043 3044 def output_disasm_range( 3045 self, f, start_address, end_address, highlight_address, exact): 3046 region = self.reader.FindRegion(highlight_address) 3047 if start_address < region[0]: 3048 start_address = region[0] 3049 if end_address > region[0] + region[1]: 3050 end_address = region[0] + region[1] 3051 count = end_address - start_address 3052 lines = self.reader.GetDisasmLines(start_address, count) 3053 found = False 3054 if exact: 3055 for line in lines: 3056 if line[0] + start_address == highlight_address: 3057 found = True 3058 break 3059 if not found: 3060 start_address = highlight_address 3061 count = end_address - start_address 3062 lines = self.reader.GetDisasmLines(highlight_address, count) 3063 expand = "" 3064 if start_address != region[0] or end_address != region[0] + region[1]: 3065 exactness = "" 3066 if exact and not found and end_address == region[0] + region[1]: 3067 exactness = "&exact=off" 3068 expand = ("(<a href=\"disasm.html?%s%s" 3069 "&val=0x%x#highlight\">more...</a>)" % 3070 (self.encfilename, exactness, highlight_address)) 3071 3072 f.write("<h3>Disassembling 0x%x - 0x%x, highlighting 0x%x %s</h3>" % 3073 (start_address, end_address, highlight_address, expand)) 3074 f.write('<div class="code">') 3075 f.write("<table class=\"codedump\">"); 3076 for i in xrange(len(lines)): 3077 line = lines[i] 3078 next_address = count 3079 if i + 1 < len(lines): 3080 next_line = lines[i + 1] 3081 next_address = next_line[0] 3082 self.format_disasm_line( 3083 f, start_address, line, next_address, highlight_address) 3084 f.write("</table>") 3085 f.write("</div>") 3086 return 3087 3088 def annotate_disasm_addresses(self, line): 3089 extra = [] 3090 for m in ADDRESS_RE.finditer(line): 3091 maybe_address = int(m.group(0), 16) 3092 formatted_address = self.format_address(maybe_address, m.group(0)) 3093 line = line.replace(m.group(0), formatted_address) 3094 object_info = self.padawan.SenseObject(maybe_address) 3095 if not object_info: 3096 continue 3097 extra.append(cgi.escape(str(object_info))) 3098 if len(extra) == 0: 3099 return line 3100 return ("%s <span class=disasmcomment>;; %s</span>" % 3101 (line, ", ".join(extra))) 3102 3103 def format_disasm_line( 3104 self, f, start, line, next_address, highlight_address): 3105 line_address = start + line[0] 3106 address_fmt = " <td>%s</td>" 3107 if line_address == highlight_address: 3108 f.write("<tr class=highlight-line>") 3109 address_fmt = " <td><a id=highlight>%s</a></td>" 3110 elif (line_address < highlight_address and 3111 highlight_address < next_address + start): 3112 f.write("<tr class=inexact-highlight-line>") 3113 address_fmt = " <td><a id=highlight>%s</a></td>" 3114 else: 3115 f.write("<tr>") 3116 num_bytes = next_address - line[0] 3117 stack_slot = self.heap.stack_map.get(line_address) 3118 marker = "" 3119 if stack_slot: 3120 marker = "=>" 3121 3122 code = line[1] 3123 3124 # Some disassemblers insert spaces between each byte, 3125 # while some do not. 3126 if code[2] == " ": 3127 op_offset = 3 * num_bytes - 1 3128 else: 3129 op_offset = 2 * num_bytes 3130 3131 # Compute the actual call target which the disassembler is too stupid 3132 # to figure out (it adds the call offset to the disassembly offset rather 3133 # than the absolute instruction address). 3134 if self.heap.reader.arch == MD_CPU_ARCHITECTURE_X86: 3135 if code.startswith("e8"): 3136 words = code.split() 3137 if len(words) > 6 and words[5] == "call": 3138 offset = int(words[4] + words[3] + words[2] + words[1], 16) 3139 target = (line_address + offset + 5) & 0xFFFFFFFF 3140 code = code.replace(words[6], "0x%08x" % target) 3141 # TODO(jkummerow): port this hack to ARM and x64. 3142 3143 opcodes = code[:op_offset] 3144 code = self.annotate_disasm_addresses(code[op_offset:]) 3145 f.write(" <td>") 3146 self.output_comment_box(f, "codel-", line_address) 3147 f.write("</td>") 3148 f.write(address_fmt % marker) 3149 f.write(" ") 3150 self.td_from_address(f, line_address) 3151 f.write(self.format_address(line_address)) 3152 f.write(" (+0x%x)</td>" % line[0]) 3153 f.write("<td>: %s </td>" % opcodes) 3154 f.write("<td>%s</td>" % code) 3155 f.write("</tr>") 3156 3157 def output_comment_box(self, f, prefix, address): 3158 comment = self.comments.get_comment(address) 3159 value = "" 3160 if comment: 3161 value = " value=\"%s\"" % cgi.escape(comment) 3162 f.write("<input type=text class=ci " 3163 "id=%s-address-0x%s onchange=c()%s>" % 3164 (prefix, 3165 self.reader.FormatIntPtr(address), 3166 value)) 3167 3168 MAX_FOUND_RESULTS = 100 3169 3170 def output_find_results(self, f, results): 3171 f.write("Addresses") 3172 toomany = len(results) > self.MAX_FOUND_RESULTS 3173 if toomany: 3174 f.write("(found %i results, displaying only first %i)" % 3175 (len(results), self.MAX_FOUND_RESULTS)) 3176 f.write(": ") 3177 results = sorted(results) 3178 results = results[:min(len(results), self.MAX_FOUND_RESULTS)] 3179 for address in results: 3180 f.write("<span %s>%s</span>" % 3181 (self.comments.get_style_class_string(address), 3182 self.format_address(address))) 3183 if toomany: 3184 f.write("...") 3185 3186 3187 def output_page_info(self, f, page_kind, page_address, my_page_address): 3188 if my_page_address == page_address and page_address != 0: 3189 f.write("Marked first %s page." % page_kind) 3190 else: 3191 f.write("<span id=\"%spage\" style=\"display:none\">" % page_kind) 3192 f.write("Marked first %s page." % page_kind) 3193 f.write("</span>\n") 3194 f.write("<button onclick=\"onpage('%spage', '0x%x')\">" % 3195 (page_kind, my_page_address)) 3196 f.write("Mark as first %s page</button>" % page_kind) 3197 return 3198 3199 def output_search_res(self, f, straddress): 3200 try: 3201 self.output_header(f) 3202 f.write("<h3>Search results for %s</h3>" % straddress) 3203 3204 address = int(straddress, 0) 3205 3206 f.write("Comment: ") 3207 self.output_comment_box(f, "search-", address) 3208 f.write("<br>") 3209 3210 page_address = address & ~self.heap.PageAlignmentMask() 3211 3212 f.write("Page info: ") 3213 self.output_page_info(f, "old", self.padawan.known_first_old_page, \ 3214 page_address) 3215 self.output_page_info(f, "map", self.padawan.known_first_map_page, \ 3216 page_address) 3217 3218 if not self.reader.IsValidAddress(address): 3219 f.write("<h3>The contents at address %s not found in the dump.</h3>" % \ 3220 straddress) 3221 else: 3222 # Print as words 3223 self.output_words(f, address - 8, address + 32, address, "Dump") 3224 3225 # Print as ASCII 3226 f.write("<hr>") 3227 self.output_ascii(f, address, address + 256, address) 3228 3229 # Print as code 3230 f.write("<hr>") 3231 self.output_disasm_range(f, address - 16, address + 16, address, True) 3232 3233 aligned_res, unaligned_res = self.reader.FindWordList(address) 3234 3235 if len(aligned_res) > 0: 3236 f.write("<h3>Occurrences of 0x%x at aligned addresses</h3>" % 3237 address) 3238 self.output_find_results(f, aligned_res) 3239 3240 if len(unaligned_res) > 0: 3241 f.write("<h3>Occurrences of 0x%x at unaligned addresses</h3>" % \ 3242 address) 3243 self.output_find_results(f, unaligned_res) 3244 3245 if len(aligned_res) + len(unaligned_res) == 0: 3246 f.write("<h3>No occurrences of 0x%x found in the dump</h3>" % address) 3247 3248 self.output_footer(f) 3249 3250 except ValueError: 3251 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress) 3252 return 3253 3254 def output_disasm_pc(self, f): 3255 address = self.reader.ExceptionIP() 3256 if not self.reader.IsValidAddress(address): 3257 return 3258 self.output_disasm_range(f, address - 16, address + 16, address, True) 3259 3260 3261WEB_DUMPS_HEADER = """ 3262<!DOCTYPE html> 3263<html> 3264<head> 3265<meta content="text/html; charset=utf-8" http-equiv="content-type"> 3266<style media="screen" type="text/css"> 3267 3268.dumplist { 3269 border-collapse : collapse; 3270 border-spacing : 0px; 3271 font-family: monospace; 3272} 3273 3274.dumpcomments { 3275 border : 1px solid LightGray; 3276 width : 32em; 3277} 3278 3279</style> 3280 3281<script type="application/javascript"> 3282 3283var dump_str = "dump-"; 3284var dump_len = dump_str.length; 3285 3286function dump_comment() { 3287 var s = event.srcElement.id; 3288 var index = s.indexOf(dump_str); 3289 if (index >= 0) { 3290 send_dump_desc(s.substring(index + dump_len), event.srcElement.value); 3291 } 3292} 3293 3294function send_dump_desc(name, desc) { 3295 xmlhttp = new XMLHttpRequest(); 3296 name = encodeURIComponent(name) 3297 desc = encodeURIComponent(desc) 3298 xmlhttp.open("GET", 3299 "setdumpdesc?dump=" + name + 3300 "&description=" + desc, true); 3301 xmlhttp.send(); 3302} 3303 3304</script> 3305 3306<title>Dump list</title> 3307</head> 3308 3309<body> 3310""" 3311 3312WEB_DUMPS_FOOTER = """ 3313</body> 3314</html> 3315""" 3316 3317DUMP_FILE_RE = re.compile(r"[-_0-9a-zA-Z][-\._0-9a-zA-Z]*\.dmp$") 3318 3319 3320class InspectionWebServer(BaseHTTPServer.HTTPServer): 3321 def __init__(self, port_number, switches, minidump_name): 3322 BaseHTTPServer.HTTPServer.__init__( 3323 self, ('localhost', port_number), InspectionWebHandler) 3324 splitpath = os.path.split(minidump_name) 3325 self.dumppath = splitpath[0] 3326 self.dumpfilename = splitpath[1] 3327 self.default_formatter = InspectionWebFormatter( 3328 switches, minidump_name, self) 3329 self.formatters = { self.dumpfilename : self.default_formatter } 3330 self.switches = switches 3331 3332 def output_dump_desc_field(self, f, name): 3333 try: 3334 descfile = open(os.path.join(self.dumppath, name + ".desc"), "r") 3335 desc = descfile.readline() 3336 descfile.close() 3337 except IOError: 3338 desc = "" 3339 f.write("<input type=\"text\" class=\"dumpcomments\" " 3340 "id=\"dump-%s\" onchange=\"dump_comment()\" value=\"%s\">\n" % 3341 (cgi.escape(name), desc)) 3342 3343 def set_dump_desc(self, name, description): 3344 if not DUMP_FILE_RE.match(name): 3345 return False 3346 fname = os.path.join(self.dumppath, name) 3347 if not os.path.isfile(fname): 3348 return False 3349 fname = fname + ".desc" 3350 descfile = open(fname, "w") 3351 descfile.write(description) 3352 descfile.close() 3353 return True 3354 3355 def get_dump_formatter(self, name): 3356 if name is None: 3357 return self.default_formatter 3358 else: 3359 if not DUMP_FILE_RE.match(name): 3360 raise WebParameterError("Invalid name '%s'" % name) 3361 formatter = self.formatters.get(name, None) 3362 if formatter is None: 3363 try: 3364 formatter = InspectionWebFormatter( 3365 self.switches, os.path.join(self.dumppath, name), self) 3366 self.formatters[name] = formatter 3367 except IOError: 3368 raise WebParameterError("Could not open dump '%s'" % name) 3369 return formatter 3370 3371 def output_dumps(self, f): 3372 f.write(WEB_DUMPS_HEADER) 3373 f.write("<h3>List of available dumps</h3>") 3374 f.write("<table class=\"dumplist\">\n") 3375 f.write("<thead><tr>") 3376 f.write("<th>Name</th>") 3377 f.write("<th>File time</th>") 3378 f.write("<th>Comment</th>") 3379 f.write("</tr></thead>") 3380 dumps_by_time = {} 3381 for fname in os.listdir(self.dumppath): 3382 if DUMP_FILE_RE.match(fname): 3383 mtime = os.stat(os.path.join(self.dumppath, fname)).st_mtime 3384 fnames = dumps_by_time.get(mtime, []) 3385 fnames.append(fname) 3386 dumps_by_time[mtime] = fnames 3387 3388 for mtime in sorted(dumps_by_time, reverse=True): 3389 fnames = dumps_by_time[mtime] 3390 for fname in fnames: 3391 f.write("<tr>\n") 3392 f.write("<td><a href=\"summary.html?%s\">%s</a></td>\n" % ( 3393 (urllib.urlencode({ 'dump' : fname }), fname))) 3394 f.write("<td> ") 3395 f.write(datetime.datetime.fromtimestamp(mtime)) 3396 f.write("</td>") 3397 f.write("<td> ") 3398 self.output_dump_desc_field(f, fname) 3399 f.write("</td>") 3400 f.write("</tr>\n") 3401 f.write("</table>\n") 3402 f.write(WEB_DUMPS_FOOTER) 3403 return 3404 3405class InspectionShell(cmd.Cmd): 3406 def __init__(self, reader, heap): 3407 cmd.Cmd.__init__(self) 3408 self.reader = reader 3409 self.heap = heap 3410 self.padawan = InspectionPadawan(reader, heap) 3411 self.prompt = "(grok) " 3412 3413 self.dd_start = 0 3414 self.dd_num = 0x10 3415 self.u_start = 0 3416 self.u_num = 0 3417 3418 def EvalExpression(self, expr): 3419 # Auto convert hex numbers to a python compatible format 3420 if expr[:2] == "00": 3421 expr = "0x"+expr 3422 result = None 3423 try: 3424 # Ugly hack to patch in register values. 3425 registers = [register 3426 for register,value in self.reader.ContextDescriptor().fields] 3427 registers.sort(key=lambda r: len(r)) 3428 registers.reverse() 3429 for register in registers: 3430 expr = expr.replace("$"+register, str(self.reader.Register(register))) 3431 result = eval(expr) 3432 except Exception as e: 3433 print("**** Could not evaluate '%s': %s" % (expr, e)) 3434 raise e 3435 return result 3436 3437 def ParseAddressExpr(self, expr): 3438 address = 0; 3439 try: 3440 result = self.EvalExpression(expr) 3441 except: 3442 return 0 3443 try: 3444 address = int(result) 3445 except Exception as e: 3446 print("**** Could not convert '%s' => %s to valid address: %s" % ( 3447 expr, result , e)) 3448 return address 3449 3450 def do_help(self, cmd=None): 3451 if len(cmd) == 0: 3452 print "Available commands" 3453 print "=" * 79 3454 prefix = "do_" 3455 methods = inspect.getmembers(InspectionShell, predicate=inspect.ismethod) 3456 for name,method in methods: 3457 if not name.startswith(prefix): continue 3458 doc = inspect.getdoc(method) 3459 if not doc: continue 3460 name = prefix.join(name.split(prefix)[1:]) 3461 description = doc.splitlines()[0] 3462 print (name + ": ").ljust(16) + description 3463 print "=" * 79 3464 else: 3465 return super(InspectionShell, self).do_help(cmd) 3466 3467 def do_p(self, cmd): 3468 """ see print """ 3469 return self.do_print(cmd) 3470 3471 def do_print(self, cmd): 3472 """ 3473 Evaluate an arbitrary python command. 3474 """ 3475 try: 3476 print(self.EvalExpression(cmd)) 3477 except: 3478 pass 3479 3480 def do_da(self, address): 3481 """ see display_ascii""" 3482 return self.do_display_ascii(address) 3483 3484 def do_display_ascii(self, address): 3485 """ 3486 Print ASCII string starting at specified address. 3487 """ 3488 address = self.ParseAddressExpr(address) 3489 string = self.reader.ReadAsciiString(address) 3490 if string == "": 3491 print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address) 3492 else: 3493 print "%s\n" % string 3494 3495 def do_dsa(self, address): 3496 """ see display_stack_ascii""" 3497 return self.do_display_stack_ascii(address) 3498 3499 def do_display_stack_ascii(self, address): 3500 """ 3501 Print ASCII stack error message. 3502 """ 3503 if self.reader.exception is None: 3504 print "Minidump has no exception info" 3505 return 3506 if len(address) == 0: 3507 address = None 3508 else: 3509 address = self.ParseAddressExpr(address) 3510 self.padawan.PrintStackTraceMessage(address) 3511 3512 def do_dd(self, args): 3513 """ 3514 Interpret memory in the given region [address, address + num * word_size) 3515 3516 (if available) as a sequence of words. Automatic alignment is not performed. 3517 If the num is not specified, a default value of 16 words is usif not self.Is 3518 If no address is given, dd continues printing at the next word. 3519 3520 Synopsis: dd 0x<address>|$register [0x<num>] 3521 """ 3522 if len(args) != 0: 3523 args = args.split(' ') 3524 self.dd_start = self.ParseAddressExpr(args[0]) 3525 self.dd_num = int(args[1], 16) if len(args) > 1 else 0x10 3526 else: 3527 self.dd_start += self.dd_num * self.reader.PointerSize() 3528 if not self.reader.IsAlignedAddress(self.dd_start): 3529 print "Warning: Dumping un-aligned memory, is this what you had in mind?" 3530 end = self.dd_start + self.reader.PointerSize() * self.dd_num 3531 self.padawan.InterpretMemory(self.dd_start, end) 3532 3533 def do_do(self, address): 3534 """ see display_object """ 3535 return self.do_display_object(address) 3536 3537 def do_display_object(self, address): 3538 """ 3539 Interpret memory at the given address as a V8 object. 3540 3541 Automatic alignment makes sure that you can pass tagged as well as 3542 un-tagged addresses. 3543 """ 3544 address = self.ParseAddressExpr(address) 3545 if self.reader.IsAlignedAddress(address): 3546 address = address + 1 3547 elif not self.heap.IsTaggedObjectAddress(address): 3548 print "Address doesn't look like a valid pointer!" 3549 return 3550 heap_object = self.padawan.SenseObject(address) 3551 if heap_object: 3552 heap_object.Print(Printer()) 3553 else: 3554 print "Address cannot be interpreted as object!" 3555 3556 def do_dso(self, args): 3557 """ see display_stack_objects """ 3558 return self.do_display_stack_objects(args) 3559 3560 def do_display_stack_objects(self, args): 3561 """ 3562 Find and Print object pointers in the given range. 3563 3564 Print all possible object pointers that are on the stack or in the given 3565 address range. 3566 3567 Usage: dso [START_ADDR,[END_ADDR]] 3568 """ 3569 start = self.reader.StackTop() 3570 end = self.reader.StackBottom() 3571 if len(args) != 0: 3572 args = args.split(' ') 3573 start = self.ParseAddressExpr(args[0]) 3574 end = self.ParseAddressExpr(args[1]) if len(args) > 1 else end 3575 objects = self.heap.FindObjectPointers(start, end) 3576 for address in objects: 3577 heap_object = self.padawan.SenseObject(address) 3578 info = "" 3579 if heap_object: 3580 info = str(heap_object) 3581 print("%s %s" % (self.padawan.FormatIntPtr(address), info)) 3582 3583 def do_do_desc(self, address): 3584 """ 3585 Print a descriptor array in a readable format. 3586 """ 3587 start = self.ParseAddressExpr(address) 3588 if ((start & 1) == 1): start = start - 1 3589 DescriptorArray(FixedArray(self.heap, None, start)).Print(Printer()) 3590 3591 def do_do_map(self, address): 3592 """ 3593 Print a Map in a readable format. 3594 """ 3595 start = self.ParseAddressExpr(address) 3596 if ((start & 1) == 1): start = start - 1 3597 Map(self.heap, None, start).Print(Printer()) 3598 3599 def do_do_trans(self, address): 3600 """ 3601 Print a transition array in a readable format. 3602 """ 3603 start = self.ParseAddressExpr(address) 3604 if ((start & 1) == 1): start = start - 1 3605 TransitionArray(FixedArray(self.heap, None, start)).Print(Printer()) 3606 3607 def do_dp(self, address): 3608 """ see display_page """ 3609 return self.do_display_page(address) 3610 3611 def do_display_page(self, address): 3612 """ 3613 Prints details about the V8 heap page of the given address. 3614 3615 Interpret memory at the given address as being on a V8 heap page 3616 and print information about the page header (if available). 3617 """ 3618 address = self.ParseAddressExpr(address) 3619 page_address = address & ~self.heap.PageAlignmentMask() 3620 if self.reader.IsValidAddress(page_address): 3621 print "**** Not Implemented" 3622 return 3623 else: 3624 print "Page header is not available!" 3625 3626 def do_k(self, arguments): 3627 """ 3628 Teach V8 heap layout information to the inspector. 3629 3630 This increases the amount of annotations the inspector can produce while 3631 dumping data. The first page of each heap space is of particular interest 3632 because it contains known objects that do not move. 3633 """ 3634 self.padawan.PrintKnowledge() 3635 3636 def do_ko(self, address): 3637 """ see known_oldspace """ 3638 return self.do_known_oldspace(address) 3639 3640 def do_known_oldspace(self, address): 3641 """ 3642 Teach V8 heap layout information to the inspector. 3643 3644 Set the first old space page by passing any pointer into that page. 3645 """ 3646 address = self.ParseAddressExpr(address) 3647 page_address = address & ~self.heap.PageAlignmentMask() 3648 self.padawan.known_first_old_page = page_address 3649 3650 def do_km(self, address): 3651 """ see known_map """ 3652 return self.do_known_map(address) 3653 3654 def do_known_map(self, address): 3655 """ 3656 Teach V8 heap layout information to the inspector. 3657 3658 Set the first map-space page by passing any pointer into that page. 3659 """ 3660 address = self.ParseAddressExpr(address) 3661 page_address = address & ~self.heap.PageAlignmentMask() 3662 self.padawan.known_first_map_page = page_address 3663 3664 def do_list(self, smth): 3665 """ 3666 List all available memory regions. 3667 """ 3668 def print_region(reader, start, size, location): 3669 print " %s - %s (%d bytes)" % (reader.FormatIntPtr(start), 3670 reader.FormatIntPtr(start + size), 3671 size) 3672 print "Available memory regions:" 3673 self.reader.ForEachMemoryRegion(print_region) 3674 3675 def do_lm(self, arg): 3676 """ see list_modules """ 3677 return self.do_list_modules(arg) 3678 3679 def do_list_modules(self, arg): 3680 """ 3681 List details for all loaded modules in the minidump. 3682 3683 An argument can be passed to limit the output to only those modules that 3684 contain the argument as a substring (case insensitive match). 3685 """ 3686 for module in self.reader.module_list.modules: 3687 if arg: 3688 name = GetModuleName(self.reader, module).lower() 3689 if name.find(arg.lower()) >= 0: 3690 PrintModuleDetails(self.reader, module) 3691 else: 3692 PrintModuleDetails(self.reader, module) 3693 print 3694 3695 def do_s(self, word): 3696 """ see search """ 3697 return self.do_search(word) 3698 3699 def do_search(self, word): 3700 """ 3701 Search for a given word in available memory regions. 3702 3703 The given word is expanded to full pointer size and searched at aligned 3704 as well as un-aligned memory locations. Use 'sa' to search aligned locations 3705 only. 3706 """ 3707 try: 3708 word = self.ParseAddressExpr(word) 3709 except ValueError: 3710 print "Malformed word, prefix with '0x' to use hexadecimal format." 3711 return 3712 print "Searching for word %d/0x%s:" % (word, self.reader.FormatIntPtr(word)) 3713 self.reader.FindWord(word) 3714 3715 def do_sh(self, none): 3716 """ 3717 Search for the V8 Heap object in all available memory regions. 3718 3719 You might get lucky and find this rare treasure full of invaluable 3720 information. 3721 """ 3722 print "**** Not Implemented" 3723 3724 def do_u(self, args): 3725 """ see disassemble """ 3726 return self.do_disassemble(args) 3727 3728 def do_disassemble(self, args): 3729 """ 3730 Unassemble memory in the region [address, address + size). 3731 3732 If the size is not specified, a default value of 32 bytes is used. 3733 Synopsis: u 0x<address> 0x<size> 3734 """ 3735 if len(args) != 0: 3736 args = args.split(' ') 3737 self.u_start = self.ParseAddressExpr(args[0]) 3738 self.u_size = self.ParseAddressExpr(args[1]) if len(args) > 1 else 0x20 3739 skip = False 3740 else: 3741 # Skip the first instruction if we reuse the last address. 3742 skip = True 3743 3744 if not self.reader.IsValidAddress(self.u_start): 3745 print "Address %s is not contained within the minidump!" % ( 3746 self.reader.FormatIntPtr(self.u_start)) 3747 return 3748 lines = self.reader.GetDisasmLines(self.u_start, self.u_size) 3749 if len(lines) == 0: 3750 print "Address %s could not be disassembled!" % ( 3751 self.reader.FormatIntPtr(self.u_start)) 3752 print " Could not disassemble using %s." % OBJDUMP_BIN 3753 print " Pass path to architecture specific objdump via --objdump?" 3754 return 3755 for line in lines: 3756 if skip: 3757 skip = False 3758 continue 3759 print FormatDisasmLine(self.u_start, self.heap, line) 3760 # Set the next start address = last line 3761 self.u_start += lines[-1][0] 3762 print 3763 3764 def do_EOF(self, none): 3765 raise KeyboardInterrupt 3766 3767EIP_PROXIMITY = 64 3768 3769CONTEXT_FOR_ARCH = { 3770 MD_CPU_ARCHITECTURE_AMD64: 3771 ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip', 3772 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'], 3773 MD_CPU_ARCHITECTURE_ARM: 3774 ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 3775 'r10', 'r11', 'r12', 'sp', 'lr', 'pc'], 3776 MD_CPU_ARCHITECTURE_ARM64: 3777 ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 3778 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 'r16', 'r17', 'r18', 'r19', 3779 'r20', 'r21', 'r22', 'r23', 'r24', 'r25', 'r26', 'r27', 'r28', 3780 'fp', 'lr', 'sp', 'pc'], 3781 MD_CPU_ARCHITECTURE_X86: 3782 ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip'] 3783} 3784 3785KNOWN_MODULES = {'chrome.exe', 'chrome.dll'} 3786 3787def GetVersionString(ms, ls): 3788 return "%d.%d.%d.%d" % (ms >> 16, ms & 0xffff, ls >> 16, ls & 0xffff) 3789 3790 3791def GetModuleName(reader, module): 3792 name = reader.ReadMinidumpString(module.module_name_rva) 3793 # simplify for path manipulation 3794 name = name.encode('utf-8') 3795 return str(os.path.basename(str(name).replace("\\", "/"))) 3796 3797 3798def PrintModuleDetails(reader, module): 3799 print "%s" % GetModuleName(reader, module) 3800 file_version = GetVersionString(module.version_info.dwFileVersionMS, 3801 module.version_info.dwFileVersionLS); 3802 product_version = GetVersionString(module.version_info.dwProductVersionMS, 3803 module.version_info.dwProductVersionLS) 3804 print " base: %s" % reader.FormatIntPtr(module.base_of_image) 3805 print " end: %s" % reader.FormatIntPtr(module.base_of_image + 3806 module.size_of_image) 3807 print " file version: %s" % file_version 3808 print " product version: %s" % product_version 3809 time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp) 3810 print " timestamp: %s" % time_date_stamp 3811 3812 3813def AnalyzeMinidump(options, minidump_name): 3814 reader = MinidumpReader(options, minidump_name) 3815 heap = None 3816 3817 stack_top = reader.ExceptionSP() 3818 stack_bottom = reader.StackBottom() 3819 stack_map = {reader.ExceptionIP(): -1} 3820 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): 3821 maybe_address = reader.ReadUIntPtr(slot) 3822 if not maybe_address in stack_map: 3823 stack_map[maybe_address] = slot 3824 3825 heap = V8Heap(reader, stack_map) 3826 padawan = InspectionPadawan(reader, heap) 3827 3828 DebugPrint("========================================") 3829 if reader.exception is None: 3830 print "Minidump has no exception info" 3831 else: 3832 print "Address markers:" 3833 print " T = valid tagged pointer in the minidump" 3834 print " S = address on the exception stack" 3835 print " C = address in loaded C/C++ module" 3836 print " * = address in the minidump" 3837 print "" 3838 print "Exception info:" 3839 exception_thread = reader.ExceptionThread() 3840 print " thread id: %d" % exception_thread.id 3841 print " code: %08X" % reader.exception.exception.code 3842 print " context:" 3843 context = CONTEXT_FOR_ARCH[reader.arch] 3844 maxWidth = max(map(lambda s: len(s), context)) 3845 for r in context: 3846 register_value = reader.Register(r) 3847 print " %s: %s" % (r.rjust(maxWidth), 3848 heap.FormatIntPtr(register_value)) 3849 # TODO(vitalyr): decode eflags. 3850 if reader.arch in [MD_CPU_ARCHITECTURE_ARM, MD_CPU_ARCHITECTURE_ARM64]: 3851 print " cpsr: %s" % bin(reader.exception_context.cpsr)[2:] 3852 else: 3853 print " eflags: %s" % bin(reader.exception_context.eflags)[2:] 3854 3855 print 3856 print " modules:" 3857 for module in reader.module_list.modules: 3858 name = GetModuleName(reader, module) 3859 if name in KNOWN_MODULES: 3860 print " %s at %08X" % (name, module.base_of_image) 3861 reader.TryLoadSymbolsFor(name, module) 3862 print 3863 3864 print " stack-top: %s" % heap.FormatIntPtr(reader.StackTop()) 3865 print " stack-bottom: %s" % heap.FormatIntPtr(reader.StackBottom()) 3866 print "" 3867 3868 if options.shell: 3869 padawan.PrintStackTraceMessage(print_message=False) 3870 3871 print "Disassembly around exception.eip:" 3872 eip_symbol = reader.FindSymbol(reader.ExceptionIP()) 3873 if eip_symbol is not None: 3874 print eip_symbol 3875 disasm_start = reader.ExceptionIP() - EIP_PROXIMITY 3876 disasm_bytes = 2 * EIP_PROXIMITY 3877 if (options.full): 3878 full_range = reader.FindRegion(reader.ExceptionIP()) 3879 if full_range is not None: 3880 disasm_start = full_range[0] 3881 disasm_bytes = full_range[1] 3882 3883 lines = reader.GetDisasmLines(disasm_start, disasm_bytes) 3884 3885 if not lines: 3886 print "Could not disassemble using %s." % OBJDUMP_BIN 3887 print "Pass path to architecture specific objdump via --objdump?" 3888 3889 for line in lines: 3890 print FormatDisasmLine(disasm_start, heap, line) 3891 print 3892 3893 if heap is None: 3894 heap = V8Heap(reader, None) 3895 3896 if options.full: 3897 FullDump(reader, heap) 3898 3899 if options.command: 3900 InspectionShell(reader, heap).onecmd(options.command) 3901 3902 if options.shell: 3903 try: 3904 InspectionShell(reader, heap).cmdloop("type help to get help") 3905 except KeyboardInterrupt: 3906 print "Kthxbye." 3907 elif not options.command: 3908 if reader.exception is not None: 3909 print "Annotated stack (from exception.esp to bottom):" 3910 stack_start = padawan.PrintStackTraceMessage() 3911 padawan.InterpretMemory(stack_start, stack_bottom) 3912 reader.Dispose() 3913 3914 3915if __name__ == "__main__": 3916 parser = optparse.OptionParser(USAGE) 3917 parser.add_option("-s", "--shell", dest="shell", action="store_true", 3918 help="start an interactive inspector shell") 3919 parser.add_option("-w", "--web", dest="web", action="store_true", 3920 help="start a web server on localhost:%i" % PORT_NUMBER) 3921 parser.add_option("-c", "--command", dest="command", default="", 3922 help="run an interactive inspector shell command and exit") 3923 parser.add_option("-f", "--full", dest="full", action="store_true", 3924 help="dump all information contained in the minidump") 3925 parser.add_option("--symdir", dest="symdir", default=".", 3926 help="directory containing *.pdb.sym file with symbols") 3927 parser.add_option("--objdump", default="", 3928 help="objdump tool to use [default: %s]" % ( 3929 DEFAULT_OBJDUMP_BIN)) 3930 options, args = parser.parse_args() 3931 if len(args) != 1: 3932 parser.print_help() 3933 sys.exit(1) 3934 if options.web: 3935 try: 3936 server = InspectionWebServer(PORT_NUMBER, options, args[0]) 3937 print 'Started httpserver on port ' , PORT_NUMBER 3938 webbrowser.open('http://localhost:%i/summary.html' % PORT_NUMBER) 3939 server.serve_forever() 3940 except KeyboardInterrupt: 3941 print '^C received, shutting down the web server' 3942 server.socket.close() 3943 else: 3944 AnalyzeMinidump(options, args[0]) 3945