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