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