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
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.
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.
45 USAGE="""usage: %prog [OPTIONS] [DUMP-FILE]
49 Shows the processor state at the point of exception including the
50 stack of the active thread and the referenced objects in the V8
51 heap. Code objects are disassembled and the addresses linked from the
52 stack (e.g. pushed return addresses) are marked with "=>".
55 $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp"""
66 class Descriptor(object):
67 """Descriptor of a structure in a memory."""
69 def __init__(self, fields):
71 self.is_flexible = False
72 for _, type_or_func in fields:
73 if isinstance(type_or_func, types.FunctionType):
74 self.is_flexible = True
76 if not self.is_flexible:
77 self.ctype = Descriptor._GetCtype(fields)
78 self.size = ctypes.sizeof(self.ctype)
80 def Read(self, memory, offset):
82 fields_copy = self.fields[:]
84 for name, type_or_func in fields_copy:
85 if isinstance(type_or_func, types.FunctionType):
86 partial_ctype = Descriptor._GetCtype(fields_copy[:last])
87 partial_object = partial_ctype.from_buffer(memory, offset)
88 type = type_or_func(partial_object)
90 fields_copy[last] = (name, type)
94 complete_ctype = Descriptor._GetCtype(fields_copy[:last])
96 complete_ctype = self.ctype
97 return complete_ctype.from_buffer(memory, offset)
100 def _GetCtype(fields):
101 class Raw(ctypes.Structure):
106 return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field))
107 for field, _ in Raw._fields_) + "}"
111 def FullDump(reader, heap):
112 """Dump all available memory regions."""
113 def dump_region(reader, start, size, location):
115 while start & 3 != 0:
119 is_executable = reader.IsProbableExecutableRegion(location, size)
120 is_ascii = reader.IsProbableASCIIRegion(location, size)
122 if is_executable is not False:
123 lines = reader.GetDisasmLines(start, size)
125 print FormatDisasmLine(start, heap, line)
128 if is_ascii is not False:
129 # Output in the same format as the Unix hd command
131 for slot in xrange(location, location + size, 16):
134 for i in xrange(0, 16):
135 if slot + i < location + size:
136 byte = ctypes.c_uint8.from_buffer(reader.minidump, slot + i).value
137 if byte >= 0x20 and byte < 0x7f:
138 asc_line += chr(byte)
141 hex_line += " %02x" % (byte)
146 print "%s %s |%s|" % (reader.FormatIntPtr(addr),
151 if is_executable is not True and is_ascii is not True:
152 print "%s - %s" % (reader.FormatIntPtr(start),
153 reader.FormatIntPtr(start + size))
154 for slot in xrange(start,
156 reader.PointerSize()):
157 maybe_address = reader.ReadUIntPtr(slot)
158 heap_object = heap.FindObject(maybe_address)
159 print "%s: %s" % (reader.FormatIntPtr(slot),
160 reader.FormatIntPtr(maybe_address))
162 heap_object.Print(Printer())
165 reader.ForEachMemoryRegion(dump_region)
167 # Heap constants generated by 'make grokdump' in v8heapconst module.
168 INSTANCE_TYPES = v8heapconst.INSTANCE_TYPES
169 KNOWN_MAPS = v8heapconst.KNOWN_MAPS
170 KNOWN_OBJECTS = v8heapconst.KNOWN_OBJECTS
172 # Set of structures and constants that describe the layout of minidump
173 # files. Based on MSDN and Google Breakpad.
175 MINIDUMP_HEADER = Descriptor([
176 ("signature", ctypes.c_uint32),
177 ("version", ctypes.c_uint32),
178 ("stream_count", ctypes.c_uint32),
179 ("stream_directories_rva", ctypes.c_uint32),
180 ("checksum", ctypes.c_uint32),
181 ("time_date_stampt", ctypes.c_uint32),
182 ("flags", ctypes.c_uint64)
185 MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
186 ("data_size", ctypes.c_uint32),
187 ("rva", ctypes.c_uint32)
190 MINIDUMP_STRING = Descriptor([
191 ("length", ctypes.c_uint32),
192 ("buffer", lambda t: ctypes.c_uint8 * (t.length + 2))
195 MINIDUMP_DIRECTORY = Descriptor([
196 ("stream_type", ctypes.c_uint32),
197 ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
200 MD_EXCEPTION_MAXIMUM_PARAMETERS = 15
202 MINIDUMP_EXCEPTION = Descriptor([
203 ("code", ctypes.c_uint32),
204 ("flags", ctypes.c_uint32),
205 ("record", ctypes.c_uint64),
206 ("address", ctypes.c_uint64),
207 ("parameter_count", ctypes.c_uint32),
208 ("unused_alignment", ctypes.c_uint32),
209 ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS)
212 MINIDUMP_EXCEPTION_STREAM = Descriptor([
213 ("thread_id", ctypes.c_uint32),
214 ("unused_alignment", ctypes.c_uint32),
215 ("exception", MINIDUMP_EXCEPTION.ctype),
216 ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
221 MD_RESERVED_STREAM_0 = 1
222 MD_RESERVED_STREAM_1 = 2
223 MD_THREAD_LIST_STREAM = 3
224 MD_MODULE_LIST_STREAM = 4
225 MD_MEMORY_LIST_STREAM = 5
226 MD_EXCEPTION_STREAM = 6
227 MD_SYSTEM_INFO_STREAM = 7
228 MD_THREAD_EX_LIST_STREAM = 8
229 MD_MEMORY_64_LIST_STREAM = 9
230 MD_COMMENT_STREAM_A = 10
231 MD_COMMENT_STREAM_W = 11
232 MD_HANDLE_DATA_STREAM = 12
233 MD_FUNCTION_TABLE_STREAM = 13
234 MD_UNLOADED_MODULE_LIST_STREAM = 14
235 MD_MISC_INFO_STREAM = 15
236 MD_MEMORY_INFO_LIST_STREAM = 16
237 MD_THREAD_INFO_LIST_STREAM = 17
238 MD_HANDLE_OPERATION_LIST_STREAM = 18
240 MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80
242 MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([
243 ("control_word", ctypes.c_uint32),
244 ("status_word", ctypes.c_uint32),
245 ("tag_word", ctypes.c_uint32),
246 ("error_offset", ctypes.c_uint32),
247 ("error_selector", ctypes.c_uint32),
248 ("data_offset", ctypes.c_uint32),
249 ("data_selector", ctypes.c_uint32),
250 ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE),
251 ("cr0_npx_state", ctypes.c_uint32)
254 MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512
257 MD_CONTEXT_X86 = 0x00010000
258 MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001)
259 MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002)
260 MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004)
261 MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008)
262 MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010)
263 MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020)
265 def EnableOnFlag(type, flag):
266 return lambda o: [None, type][int((o.context_flags & flag) != 0)]
268 MINIDUMP_CONTEXT_X86 = Descriptor([
269 ("context_flags", ctypes.c_uint32),
270 # MD_CONTEXT_X86_DEBUG_REGISTERS.
271 ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
272 ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
273 ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
274 ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
275 ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
276 ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
277 # MD_CONTEXT_X86_FLOATING_POINT.
278 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype,
279 MD_CONTEXT_X86_FLOATING_POINT)),
280 # MD_CONTEXT_X86_SEGMENTS.
281 ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
282 ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
283 ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
284 ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
285 # MD_CONTEXT_X86_INTEGER.
286 ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
287 ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
288 ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
289 ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
290 ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
291 ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
292 # MD_CONTEXT_X86_CONTROL.
293 ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
294 ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
295 ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
296 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
297 ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
298 ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
299 # MD_CONTEXT_X86_EXTENDED_REGISTERS.
300 ("extended_registers",
301 EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE,
302 MD_CONTEXT_X86_EXTENDED_REGISTERS))
305 MD_CONTEXT_ARM = 0x40000000
306 MD_CONTEXT_ARM_INTEGER = (MD_CONTEXT_ARM | 0x00000002)
307 MD_CONTEXT_ARM_FLOATING_POINT = (MD_CONTEXT_ARM | 0x00000004)
308 MD_FLOATINGSAVEAREA_ARM_FPR_COUNT = 32
309 MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT = 8
311 MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([
312 ("fpscr", ctypes.c_uint64),
313 ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPR_COUNT),
314 ("extra", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT)
317 MINIDUMP_CONTEXT_ARM = Descriptor([
318 ("context_flags", ctypes.c_uint32),
319 # MD_CONTEXT_ARM_INTEGER.
320 ("r0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
321 ("r1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
322 ("r2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
323 ("r3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
324 ("r4", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
325 ("r5", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
326 ("r6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
327 ("r7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
328 ("r8", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
329 ("r9", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
330 ("r10", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
331 ("r11", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
332 ("r12", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
333 ("sp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
334 ("lr", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
335 ("pc", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
336 ("cpsr", ctypes.c_uint32),
337 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype,
338 MD_CONTEXT_ARM_FLOATING_POINT))
341 MD_CONTEXT_AMD64 = 0x00100000
342 MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001)
343 MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002)
344 MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004)
345 MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008)
346 MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010)
348 MINIDUMP_CONTEXT_AMD64 = Descriptor([
349 ("p1_home", ctypes.c_uint64),
350 ("p2_home", ctypes.c_uint64),
351 ("p3_home", ctypes.c_uint64),
352 ("p4_home", ctypes.c_uint64),
353 ("p5_home", ctypes.c_uint64),
354 ("p6_home", ctypes.c_uint64),
355 ("context_flags", ctypes.c_uint32),
356 ("mx_csr", ctypes.c_uint32),
357 # MD_CONTEXT_AMD64_CONTROL.
358 ("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
359 # MD_CONTEXT_AMD64_SEGMENTS
360 ("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
361 ("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
362 ("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
363 ("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
364 # MD_CONTEXT_AMD64_CONTROL.
365 ("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
366 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)),
367 # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
368 ("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
369 ("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
370 ("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
371 ("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
372 ("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
373 ("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
374 # MD_CONTEXT_AMD64_INTEGER.
375 ("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
376 ("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
377 ("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
378 ("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
379 # MD_CONTEXT_AMD64_CONTROL.
380 ("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
381 # MD_CONTEXT_AMD64_INTEGER.
382 ("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
383 ("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
384 ("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
385 ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
386 ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
387 ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
388 ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
389 ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
390 ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
391 ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
392 ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
393 # MD_CONTEXT_AMD64_CONTROL.
394 ("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
395 # MD_CONTEXT_AMD64_FLOATING_POINT
396 ("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
397 MD_CONTEXT_AMD64_FLOATING_POINT)),
398 ("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
399 MD_CONTEXT_AMD64_FLOATING_POINT)),
400 ("vector_control", EnableOnFlag(ctypes.c_uint64,
401 MD_CONTEXT_AMD64_FLOATING_POINT)),
402 # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
403 ("debug_control", EnableOnFlag(ctypes.c_uint64,
404 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
405 ("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64,
406 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
407 ("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64,
408 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
409 ("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64,
410 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
411 ("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64,
412 MD_CONTEXT_AMD64_DEBUG_REGISTERS))
415 MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
416 ("start", ctypes.c_uint64),
417 ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
420 MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
421 ("start", ctypes.c_uint64),
422 ("size", ctypes.c_uint64)
425 MINIDUMP_MEMORY_LIST = Descriptor([
426 ("range_count", ctypes.c_uint32),
427 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
430 MINIDUMP_MEMORY_LIST64 = Descriptor([
431 ("range_count", ctypes.c_uint64),
432 ("base_rva", ctypes.c_uint64),
433 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count)
436 MINIDUMP_THREAD = Descriptor([
437 ("id", ctypes.c_uint32),
438 ("suspend_count", ctypes.c_uint32),
439 ("priority_class", ctypes.c_uint32),
440 ("priority", ctypes.c_uint32),
441 ("ted", ctypes.c_uint64),
442 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype),
443 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
446 MINIDUMP_THREAD_LIST = Descriptor([
447 ("thread_count", ctypes.c_uint32),
448 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
451 MINIDUMP_VS_FIXEDFILEINFO = Descriptor([
452 ("dwSignature", ctypes.c_uint32),
453 ("dwStrucVersion", ctypes.c_uint32),
454 ("dwFileVersionMS", ctypes.c_uint32),
455 ("dwFileVersionLS", ctypes.c_uint32),
456 ("dwProductVersionMS", ctypes.c_uint32),
457 ("dwProductVersionLS", ctypes.c_uint32),
458 ("dwFileFlagsMask", ctypes.c_uint32),
459 ("dwFileFlags", ctypes.c_uint32),
460 ("dwFileOS", ctypes.c_uint32),
461 ("dwFileType", ctypes.c_uint32),
462 ("dwFileSubtype", ctypes.c_uint32),
463 ("dwFileDateMS", ctypes.c_uint32),
464 ("dwFileDateLS", ctypes.c_uint32)
467 MINIDUMP_RAW_MODULE = Descriptor([
468 ("base_of_image", ctypes.c_uint64),
469 ("size_of_image", ctypes.c_uint32),
470 ("checksum", ctypes.c_uint32),
471 ("time_date_stamp", ctypes.c_uint32),
472 ("module_name_rva", ctypes.c_uint32),
473 ("version_info", MINIDUMP_VS_FIXEDFILEINFO.ctype),
474 ("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
475 ("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
476 ("reserved0", ctypes.c_uint32 * 2),
477 ("reserved1", ctypes.c_uint32 * 2)
480 MINIDUMP_MODULE_LIST = Descriptor([
481 ("number_of_modules", ctypes.c_uint32),
482 ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules)
485 MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
486 ("processor_architecture", ctypes.c_uint16)
489 MD_CPU_ARCHITECTURE_X86 = 0
490 MD_CPU_ARCHITECTURE_ARM = 5
491 MD_CPU_ARCHITECTURE_AMD64 = 9
494 def __init__(self, start, size, name):
496 self.end = self.start + size
499 def __cmp__(self, other):
500 if isinstance(other, FuncSymbol):
501 return self.start - other.start
502 return self.start - other
504 def Covers(self, addr):
505 return (self.start <= addr) and (addr < self.end)
507 class MinidumpReader(object):
508 """Minidump (.dmp) reader."""
510 _HEADER_MAGIC = 0x504d444d
512 def __init__(self, options, minidump_name):
513 self.minidump_name = minidump_name
514 self.minidump_file = open(minidump_name, "r")
515 self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE)
516 self.header = MINIDUMP_HEADER.Read(self.minidump, 0)
517 if self.header.signature != MinidumpReader._HEADER_MAGIC:
518 print >>sys.stderr, "Warning: Unsupported minidump header magic!"
519 DebugPrint(self.header)
521 offset = self.header.stream_directories_rva
522 for _ in xrange(self.header.stream_count):
523 directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
524 offset += MINIDUMP_DIRECTORY.size
526 self.exception = None
527 self.exception_context = None
528 self.memory_list = None
529 self.memory_list64 = None
530 self.module_list = None
533 self.symdir = options.symdir
534 self.modules_with_symbols = []
537 # Find MDRawSystemInfo stream and determine arch.
538 for d in directories:
539 if d.stream_type == MD_SYSTEM_INFO_STREAM:
540 system_info = MINIDUMP_RAW_SYSTEM_INFO.Read(
541 self.minidump, d.location.rva)
542 self.arch = system_info.processor_architecture
543 assert self.arch in [MD_CPU_ARCHITECTURE_AMD64,
544 MD_CPU_ARCHITECTURE_ARM,
545 MD_CPU_ARCHITECTURE_X86]
546 assert not self.arch is None
548 for d in directories:
550 if d.stream_type == MD_EXCEPTION_STREAM:
551 self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
552 self.minidump, d.location.rva)
553 DebugPrint(self.exception)
554 if self.arch == MD_CPU_ARCHITECTURE_X86:
555 self.exception_context = MINIDUMP_CONTEXT_X86.Read(
556 self.minidump, self.exception.thread_context.rva)
557 elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
558 self.exception_context = MINIDUMP_CONTEXT_AMD64.Read(
559 self.minidump, self.exception.thread_context.rva)
560 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
561 self.exception_context = MINIDUMP_CONTEXT_ARM.Read(
562 self.minidump, self.exception.thread_context.rva)
563 DebugPrint(self.exception_context)
564 elif d.stream_type == MD_THREAD_LIST_STREAM:
565 thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
566 assert ctypes.sizeof(thread_list) == d.location.data_size
567 DebugPrint(thread_list)
568 for thread in thread_list.threads:
570 self.thread_map[thread.id] = thread
571 elif d.stream_type == MD_MODULE_LIST_STREAM:
572 assert self.module_list is None
573 self.module_list = MINIDUMP_MODULE_LIST.Read(
574 self.minidump, d.location.rva)
575 assert ctypes.sizeof(self.module_list) == d.location.data_size
576 elif d.stream_type == MD_MEMORY_LIST_STREAM:
577 print >>sys.stderr, "Warning: This is not a full minidump!"
578 assert self.memory_list is None
579 self.memory_list = MINIDUMP_MEMORY_LIST.Read(
580 self.minidump, d.location.rva)
581 assert ctypes.sizeof(self.memory_list) == d.location.data_size
582 DebugPrint(self.memory_list)
583 elif d.stream_type == MD_MEMORY_64_LIST_STREAM:
584 assert self.memory_list64 is None
585 self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read(
586 self.minidump, d.location.rva)
587 assert ctypes.sizeof(self.memory_list64) == d.location.data_size
588 DebugPrint(self.memory_list64)
590 def IsValidAddress(self, address):
591 return self.FindLocation(address) is not None
593 def ReadU8(self, address):
594 location = self.FindLocation(address)
595 return ctypes.c_uint8.from_buffer(self.minidump, location).value
597 def ReadU32(self, address):
598 location = self.FindLocation(address)
599 return ctypes.c_uint32.from_buffer(self.minidump, location).value
601 def ReadU64(self, address):
602 location = self.FindLocation(address)
603 return ctypes.c_uint64.from_buffer(self.minidump, location).value
605 def ReadUIntPtr(self, address):
606 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
607 return self.ReadU64(address)
608 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
609 return self.ReadU32(address)
610 elif self.arch == MD_CPU_ARCHITECTURE_X86:
611 return self.ReadU32(address)
613 def ReadBytes(self, address, size):
614 location = self.FindLocation(address)
615 return self.minidump[location:location + size]
617 def _ReadWord(self, location):
618 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
619 return ctypes.c_uint64.from_buffer(self.minidump, location).value
620 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
621 return ctypes.c_uint32.from_buffer(self.minidump, location).value
622 elif self.arch == MD_CPU_ARCHITECTURE_X86:
623 return ctypes.c_uint32.from_buffer(self.minidump, location).value
625 def IsProbableASCIIRegion(self, location, length):
628 for loc in xrange(location, location + length):
629 byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
632 if byte < 0x20 and byte != 0:
634 if byte < 0x7f and byte >= 0x20:
636 if byte == 0xa: # newline
638 if ascii_bytes * 10 <= length:
640 if length > 0 and ascii_bytes > non_ascii_bytes * 7:
642 if ascii_bytes > non_ascii_bytes * 3:
646 def IsProbableExecutableRegion(self, location, length):
648 sixty_four = self.arch == MD_CPU_ARCHITECTURE_AMD64
649 for loc in xrange(location, location + length):
650 byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
651 if (byte == 0x8b or # mov
652 byte == 0x89 or # mov reg-reg
653 (byte & 0xf0) == 0x50 or # push/pop
654 (sixty_four and (byte & 0xf0) == 0x40) or # rex prefix
655 byte == 0xc3 or # return
656 byte == 0x74 or # jeq
657 byte == 0x84 or # jeq far
658 byte == 0x75 or # jne
659 byte == 0x85 or # jne far
660 byte == 0xe8 or # call
661 byte == 0xe9 or # jmp far
662 byte == 0xeb): # jmp near
664 opcode_percent = (opcode_bytes * 100) / length
666 if opcode_percent > threshold + 2:
668 if opcode_percent > threshold - 2:
672 def FindRegion(self, addr):
674 def is_in(reader, start, size, location):
675 if addr >= start and addr < start + size:
678 self.ForEachMemoryRegion(is_in)
683 def ForEachMemoryRegion(self, cb):
684 if self.memory_list64 is not None:
685 for r in self.memory_list64.ranges:
686 location = self.memory_list64.base_rva + offset
687 cb(self, r.start, r.size, location)
690 if self.memory_list is not None:
691 for r in self.memory_list.ranges:
692 cb(self, r.start, r.memory.data_size, r.memory.rva)
694 def FindWord(self, word, alignment=0):
695 def search_inside_region(reader, start, size, location):
696 location = (location + alignment) & ~alignment
697 for loc in xrange(location, location + size - self.PointerSize()):
698 if reader._ReadWord(loc) == word:
699 slot = start + (loc - location)
700 print "%s: %s" % (reader.FormatIntPtr(slot),
701 reader.FormatIntPtr(word))
702 self.ForEachMemoryRegion(search_inside_region)
704 def FindLocation(self, address):
706 if self.memory_list64 is not None:
707 for r in self.memory_list64.ranges:
708 if r.start <= address < r.start + r.size:
709 return self.memory_list64.base_rva + offset + address - r.start
711 if self.memory_list is not None:
712 for r in self.memory_list.ranges:
713 if r.start <= address < r.start + r.memory.data_size:
714 return r.memory.rva + address - r.start
717 def GetDisasmLines(self, address, size):
718 def CountUndefinedInstructions(lines):
719 pattern = "<UNDEFINED>"
720 return sum([line.count(pattern) for (ignore, line) in lines])
722 location = self.FindLocation(address)
723 if location is None: return []
725 possible_objdump_flags = [""]
726 if self.arch == MD_CPU_ARCHITECTURE_X86:
728 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
730 possible_objdump_flags = ["", "--disassembler-options=force-thumb"]
731 elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
733 results = [ disasm.GetDisasmLines(self.minidump_name,
739 for objdump_flags in possible_objdump_flags ]
740 return min(results, key=CountUndefinedInstructions)
744 self.minidump.close()
745 self.minidump_file.close()
747 def ExceptionIP(self):
748 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
749 return self.exception_context.rip
750 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
751 return self.exception_context.pc
752 elif self.arch == MD_CPU_ARCHITECTURE_X86:
753 return self.exception_context.eip
755 def ExceptionSP(self):
756 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
757 return self.exception_context.rsp
758 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
759 return self.exception_context.sp
760 elif self.arch == MD_CPU_ARCHITECTURE_X86:
761 return self.exception_context.esp
763 def ExceptionFP(self):
764 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
765 return self.exception_context.rbp
766 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
768 elif self.arch == MD_CPU_ARCHITECTURE_X86:
769 return self.exception_context.ebp
771 def FormatIntPtr(self, value):
772 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
773 return "%016x" % value
774 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
775 return "%08x" % value
776 elif self.arch == MD_CPU_ARCHITECTURE_X86:
777 return "%08x" % value
779 def PointerSize(self):
780 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
782 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
784 elif self.arch == MD_CPU_ARCHITECTURE_X86:
787 def Register(self, name):
788 return self.exception_context.__getattribute__(name)
790 def ReadMinidumpString(self, rva):
791 string = bytearray(MINIDUMP_STRING.Read(self.minidump, rva).buffer)
792 string = string.decode("utf16")
793 return string[0:len(string) - 1]
795 # Load FUNC records from a BreakPad symbol file
797 # http://code.google.com/p/google-breakpad/wiki/SymbolFiles
799 def _LoadSymbolsFrom(self, symfile, baseaddr):
800 print "Loading symbols from %s" % (symfile)
802 with open(symfile) as f:
805 r"^FUNC ([a-f0-9]+) ([a-f0-9]+) ([a-f0-9]+) (.*)$", line)
806 if result is not None:
807 start = int(result.group(1), 16)
808 size = int(result.group(2), 16)
809 name = result.group(4).rstrip()
810 bisect.insort_left(self.symbols,
811 FuncSymbol(baseaddr + start, size, name))
814 def TryLoadSymbolsFor(self, modulename, module):
816 symfile = os.path.join(self.symdir,
817 modulename.replace('.', '_') + ".pdb.sym")
818 if os.path.isfile(symfile):
819 self._LoadSymbolsFrom(symfile, module.base_of_image)
820 self.modules_with_symbols.append(module)
821 except Exception as e:
822 print " ... failure (%s)" % (e)
824 # Returns true if address is covered by some module that has loaded symbols.
825 def _IsInModuleWithSymbols(self, addr):
826 for module in self.modules_with_symbols:
827 start = module.base_of_image
828 end = start + module.size_of_image
829 if (start <= addr) and (addr < end):
833 # Find symbol covering the given address and return its name in format
834 # <symbol name>+<offset from the start>
835 def FindSymbol(self, addr):
836 if not self._IsInModuleWithSymbols(addr):
839 i = bisect.bisect_left(self.symbols, addr)
841 if (0 < i) and self.symbols[i - 1].Covers(addr):
842 symbol = self.symbols[i - 1]
843 elif (i < len(self.symbols)) and self.symbols[i].Covers(addr):
844 symbol = self.symbols[i]
847 diff = addr - symbol.start
848 return "%s+0x%x" % (symbol.name, diff)
851 class Printer(object):
852 """Printer with indentation support."""
863 def Print(self, string):
864 print "%s%s" % (self._IndentString(), string)
866 def PrintLines(self, lines):
867 indent = self._IndentString()
868 print "\n".join("%s%s" % (indent, line) for line in lines)
870 def _IndentString(self):
871 return self.indent * " "
874 ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
877 def FormatDisasmLine(start, heap, line):
878 line_address = start + line[0]
879 stack_slot = heap.stack_map.get(line_address)
883 code = AnnotateAddresses(heap, line[1])
884 return "%s%08x %08x: %s" % (marker, line_address, line[0], code)
887 def AnnotateAddresses(heap, line):
889 for m in ADDRESS_RE.finditer(line):
890 maybe_address = int(m.group(0), 16)
891 object = heap.FindObject(maybe_address)
892 if not object: continue
893 extra.append(str(object))
894 if len(extra) == 0: return line
895 return "%s ;; %s" % (line, ", ".join(extra))
898 class HeapObject(object):
899 def __init__(self, heap, map, address):
902 self.address = address
905 return isinstance(self, cls)
911 return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address),
912 INSTANCE_TYPES[self.map.instance_type])
914 def ObjectField(self, offset):
915 field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
916 return self.heap.FindObjectOrSmi(field_value)
918 def SmiField(self, offset):
919 field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
920 assert (field_value & 1) == 0
921 return field_value / 2
924 class Map(HeapObject):
925 def Decode(self, offset, size, value):
926 return (value >> offset) & ((1 << size) - 1)
929 def InstanceSizesOffset(self):
930 return self.heap.PointerSize()
932 def InstanceSizeOffset(self):
933 return self.InstanceSizesOffset()
935 def InObjectProperties(self):
936 return self.InstanceSizeOffset() + 1
938 def PreAllocatedPropertyFields(self):
939 return self.InObjectProperties() + 1
942 return self.PreAllocatedPropertyFields() + 1
944 # Instance Attributes
945 def InstanceAttributesOffset(self):
946 return self.InstanceSizesOffset() + self.heap.IntSize()
948 def InstanceTypeOffset(self):
949 return self.InstanceAttributesOffset()
951 def UnusedPropertyFieldsOffset(self):
952 return self.InstanceTypeOffset() + 1
954 def BitFieldOffset(self):
955 return self.UnusedPropertyFieldsOffset() + 1
957 def BitField2Offset(self):
958 return self.BitFieldOffset() + 1
961 def PrototypeOffset(self):
962 return self.InstanceAttributesOffset() + self.heap.IntSize()
964 def ConstructorOffset(self):
965 return self.PrototypeOffset() + self.heap.PointerSize()
967 def TransitionsOrBackPointerOffset(self):
968 return self.ConstructorOffset() + self.heap.PointerSize()
970 def DescriptorsOffset(self):
971 return self.TransitionsOrBackPointerOffset() + self.heap.PointerSize()
973 def CodeCacheOffset(self):
974 return self.DescriptorsOffset() + self.heap.PointerSize()
976 def DependentCodeOffset(self):
977 return self.CodeCacheOffset() + self.heap.PointerSize()
979 def BitField3Offset(self):
980 return self.DependentCodeOffset() + self.heap.PointerSize()
982 def ReadByte(self, offset):
983 return self.heap.reader.ReadU8(self.address + offset)
986 p.Print("Map(%08x)" % (self.address))
987 p.Print("- size: %d, inobject: %d, preallocated: %d, visitor: %d" % (
988 self.ReadByte(self.InstanceSizeOffset()),
989 self.ReadByte(self.InObjectProperties()),
990 self.ReadByte(self.PreAllocatedPropertyFields()),
993 bitfield = self.ReadByte(self.BitFieldOffset())
994 bitfield2 = self.ReadByte(self.BitField2Offset())
995 p.Print("- %s, unused: %d, bf: %d, bf2: %d" % (
996 INSTANCE_TYPES[self.ReadByte(self.InstanceTypeOffset())],
997 self.ReadByte(self.UnusedPropertyFieldsOffset()),
998 bitfield, bitfield2))
1000 p.Print("- kind: %s" % (self.Decode(3, 5, bitfield2)))
1002 bitfield3 = self.ObjectField(self.BitField3Offset())
1004 "- EnumLength: %d NumberOfOwnDescriptors: %d OwnsDescriptors: %s" % (
1005 self.Decode(0, 11, bitfield3),
1006 self.Decode(11, 11, bitfield3),
1007 self.Decode(25, 1, bitfield3)))
1008 p.Print("- IsShared: %s" % (self.Decode(22, 1, bitfield3)))
1009 p.Print("- FunctionWithPrototype: %s" % (self.Decode(23, 1, bitfield3)))
1010 p.Print("- DictionaryMap: %s" % (self.Decode(24, 1, bitfield3)))
1012 descriptors = self.ObjectField(self.DescriptorsOffset())
1013 if descriptors.__class__ == FixedArray:
1014 DescriptorArray(descriptors).Print(p)
1016 p.Print("Descriptors: %s" % (descriptors))
1018 transitions = self.ObjectField(self.TransitionsOrBackPointerOffset())
1019 if transitions.__class__ == FixedArray:
1020 TransitionArray(transitions).Print(p)
1022 p.Print("TransitionsOrBackPointer: %s" % (transitions))
1024 def __init__(self, heap, map, address):
1025 HeapObject.__init__(self, heap, map, address)
1026 self.instance_type = \
1027 heap.reader.ReadU8(self.address + self.InstanceTypeOffset())
1030 class String(HeapObject):
1031 def LengthOffset(self):
1032 # First word after the map is the hash, the second is the length.
1033 return self.heap.PointerSize() * 2
1035 def __init__(self, heap, map, address):
1036 HeapObject.__init__(self, heap, map, address)
1037 self.length = self.SmiField(self.LengthOffset())
1046 return "\"%s\"" % self.GetChars()
1049 class SeqString(String):
1050 def CharsOffset(self):
1051 return self.heap.PointerSize() * 3
1053 def __init__(self, heap, map, address):
1054 String.__init__(self, heap, map, address)
1055 self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(),
1062 class ExternalString(String):
1063 # TODO(vegorov) fix ExternalString for X64 architecture
1064 RESOURCE_OFFSET = 12
1066 WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
1067 WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
1069 def __init__(self, heap, map, address):
1070 String.__init__(self, heap, map, address)
1071 reader = heap.reader
1073 reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
1074 self.chars = "?external string?"
1075 if not reader.IsValidAddress(self.resource): return
1076 string_impl_address = self.resource + \
1077 ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
1078 if not reader.IsValidAddress(string_impl_address): return
1079 string_impl = reader.ReadU32(string_impl_address)
1080 chars_ptr_address = string_impl + \
1081 ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
1082 if not reader.IsValidAddress(chars_ptr_address): return
1083 chars_ptr = reader.ReadU32(chars_ptr_address)
1084 if not reader.IsValidAddress(chars_ptr): return
1085 raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
1086 self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
1092 class ConsString(String):
1093 def LeftOffset(self):
1094 return self.heap.PointerSize() * 3
1096 def RightOffset(self):
1097 return self.heap.PointerSize() * 4
1099 def __init__(self, heap, map, address):
1100 String.__init__(self, heap, map, address)
1101 self.left = self.ObjectField(self.LeftOffset())
1102 self.right = self.ObjectField(self.RightOffset())
1106 return self.left.GetChars() + self.right.GetChars()
1108 return "***CAUGHT EXCEPTION IN GROKDUMP***"
1111 class Oddball(HeapObject):
1112 # Should match declarations in objects.h
1123 def ToStringOffset(self):
1124 return self.heap.PointerSize()
1126 def ToNumberOffset(self):
1127 return self.ToStringOffset() + self.heap.PointerSize()
1129 def KindOffset(self):
1130 return self.ToNumberOffset() + self.heap.PointerSize()
1132 def __init__(self, heap, map, address):
1133 HeapObject.__init__(self, heap, map, address)
1134 self.to_string = self.ObjectField(self.ToStringOffset())
1135 self.kind = self.SmiField(self.KindOffset())
1142 return "Oddball(%08x, <%s>)" % (self.address, str(self.to_string))
1145 if 0 <= self.kind < len(Oddball.KINDS):
1146 kind = Oddball.KINDS[self.kind]
1147 return "Oddball(%08x, kind=%s)" % (self.address, kind)
1150 class FixedArray(HeapObject):
1151 def LengthOffset(self):
1152 return self.heap.PointerSize()
1154 def ElementsOffset(self):
1155 return self.heap.PointerSize() * 2
1157 def MemberOffset(self, i):
1158 return self.ElementsOffset() + self.heap.PointerSize() * i
1161 return self.ObjectField(self.MemberOffset(i))
1163 def __init__(self, heap, map, address):
1164 HeapObject.__init__(self, heap, map, address)
1165 self.length = self.SmiField(self.LengthOffset())
1168 p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1170 p.Print("length: %d" % self.length)
1171 base_offset = self.ElementsOffset()
1172 for i in xrange(self.length):
1173 offset = base_offset + 4 * i
1175 p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
1185 return "FixedArray(%08x, length=%d)" % (self.address, self.length)
1188 class DescriptorArray(object):
1189 def __init__(self, array):
1193 return self.array.Get(0)
1195 def Decode(self, offset, size, value):
1196 return (value >> offset) & ((1 << size) - 1)
1205 def Type(self, value):
1206 return DescriptorArray.TYPES[self.Decode(0, 3, value)]
1208 def Attributes(self, value):
1209 attributes = self.Decode(3, 3, value)
1211 if (attributes & 0): result += ["ReadOnly"]
1212 if (attributes & 1): result += ["DontEnum"]
1213 if (attributes & 2): result += ["DontDelete"]
1214 return "[" + (",".join(result)) + "]"
1216 def Deleted(self, value):
1217 return self.Decode(6, 1, value) == 1
1219 def FieldIndex(self, value):
1220 return self.Decode(20, 11, value)
1222 def Pointer(self, value):
1223 return self.Decode(6, 11, value)
1225 def Details(self, di, value):
1229 self.Attributes(value),
1230 self.FieldIndex(value),
1236 length = self.Length()
1239 p.Print("Descriptors(%08x, length=%d)" % (array.address, length))
1240 p.Print("[et] %s" % (array.Get(1)))
1242 for di in xrange(length):
1244 p.Print("0x%x" % (array.address + array.MemberOffset(i)))
1245 p.Print("[%i] name: %s" % (di, array.Get(i + 0)))
1246 p.Print("[%i] details: %s %s field-index %i pointer %i" % \
1247 self.Details(di, array.Get(i + 1)))
1248 p.Print("[%i] value: %s" % (di, array.Get(i + 2)))
1250 end = self.array.length // 3
1252 p.Print("[%i-%i] slack descriptors" % (length, end))
1255 class TransitionArray(object):
1256 def __init__(self, array):
1259 def IsSimpleTransition(self):
1260 return self.array.length <= 2
1263 # SimpleTransition cases
1264 if self.IsSimpleTransition():
1265 return self.array.length - 1
1266 return (self.array.length - 3) // 2
1269 length = self.Length()
1272 p.Print("Transitions(%08x, length=%d)" % (array.address, length))
1273 p.Print("[backpointer] %s" % (array.Get(0)))
1274 if self.IsSimpleTransition():
1276 p.Print("[simple target] %s" % (array.Get(1)))
1279 elements = array.Get(1)
1280 if elements is not None:
1281 p.Print("[elements ] %s" % (elements))
1283 prototype = array.Get(2)
1284 if prototype is not None:
1285 p.Print("[prototype ] %s" % (prototype))
1287 for di in xrange(length):
1289 p.Print("[%i] symbol: %s" % (di, array.Get(i + 0)))
1290 p.Print("[%i] target: %s" % (di, array.Get(i + 1)))
1293 class JSFunction(HeapObject):
1294 def CodeEntryOffset(self):
1295 return 3 * self.heap.PointerSize()
1297 def SharedOffset(self):
1298 return 5 * self.heap.PointerSize()
1300 def __init__(self, heap, map, address):
1301 HeapObject.__init__(self, heap, map, address)
1303 heap.reader.ReadU32(self.address + self.CodeEntryOffset())
1304 self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1)
1305 self.shared = self.ObjectField(self.SharedOffset())
1308 source = "\n".join(" %s" % line for line in self._GetSource().split("\n"))
1309 p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1311 p.Print("inferred name: %s" % self.shared.inferred_name)
1312 if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
1313 p.Print("script name: %s" % self.shared.script.name)
1315 p.PrintLines(self._GetSource().split("\n"))
1318 if self.code != self.shared.code:
1319 p.Print("unoptimized code:")
1320 self.shared.code.Print(p)
1326 if self.shared.Is(SharedFunctionInfo):
1327 inferred_name = self.shared.inferred_name
1328 return "JSFunction(%s, %s)" % \
1329 (self.heap.reader.FormatIntPtr(self.address), inferred_name)
1331 def _GetSource(self):
1333 start = self.shared.start_position
1334 end = self.shared.end_position
1335 if not self.shared.script.Is(Script): return source
1336 script_source = self.shared.script.source
1337 if not script_source.Is(String): return source
1338 return script_source.GetChars()[start:end]
1341 class SharedFunctionInfo(HeapObject):
1342 def CodeOffset(self):
1343 return 2 * self.heap.PointerSize()
1345 def ScriptOffset(self):
1346 return 7 * self.heap.PointerSize()
1348 def InferredNameOffset(self):
1349 return 9 * self.heap.PointerSize()
1351 def EndPositionOffset(self):
1352 return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
1354 def StartPositionAndTypeOffset(self):
1355 return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
1357 def __init__(self, heap, map, address):
1358 HeapObject.__init__(self, heap, map, address)
1359 self.code = self.ObjectField(self.CodeOffset())
1360 self.script = self.ObjectField(self.ScriptOffset())
1361 self.inferred_name = self.ObjectField(self.InferredNameOffset())
1362 if heap.PointerSize() == 8:
1363 start_position_and_type = \
1364 heap.reader.ReadU32(self.StartPositionAndTypeOffset())
1365 self.start_position = start_position_and_type >> 2
1366 pseudo_smi_end_position = \
1367 heap.reader.ReadU32(self.EndPositionOffset())
1368 self.end_position = pseudo_smi_end_position >> 2
1370 start_position_and_type = \
1371 self.SmiField(self.StartPositionAndTypeOffset())
1372 self.start_position = start_position_and_type >> 2
1373 self.end_position = \
1374 self.SmiField(self.EndPositionOffset())
1377 class Script(HeapObject):
1378 def SourceOffset(self):
1379 return self.heap.PointerSize()
1381 def NameOffset(self):
1382 return self.SourceOffset() + self.heap.PointerSize()
1384 def __init__(self, heap, map, address):
1385 HeapObject.__init__(self, heap, map, address)
1386 self.source = self.ObjectField(self.SourceOffset())
1387 self.name = self.ObjectField(self.NameOffset())
1390 class CodeCache(HeapObject):
1391 def DefaultCacheOffset(self):
1392 return self.heap.PointerSize()
1394 def NormalTypeCacheOffset(self):
1395 return self.DefaultCacheOffset() + self.heap.PointerSize()
1397 def __init__(self, heap, map, address):
1398 HeapObject.__init__(self, heap, map, address)
1399 self.default_cache = self.ObjectField(self.DefaultCacheOffset())
1400 self.normal_type_cache = self.ObjectField(self.NormalTypeCacheOffset())
1403 p.Print("CodeCache(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1405 p.Print("default cache: %s" % self.default_cache)
1406 p.Print("normal type cache: %s" % self.normal_type_cache)
1411 class Code(HeapObject):
1412 CODE_ALIGNMENT_MASK = (1 << 5) - 1
1414 def InstructionSizeOffset(self):
1415 return self.heap.PointerSize()
1418 def HeaderSize(heap):
1419 return (heap.PointerSize() + heap.IntSize() + \
1420 4 * heap.PointerSize() + 3 * heap.IntSize() + \
1421 Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK
1423 def __init__(self, heap, map, address):
1424 HeapObject.__init__(self, heap, map, address)
1425 self.entry = self.address + Code.HeaderSize(heap)
1426 self.instruction_size = \
1427 heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
1430 lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
1431 p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1433 p.Print("instruction_size: %d" % self.instruction_size)
1434 p.PrintLines(self._FormatLine(line) for line in lines)
1438 def _FormatLine(self, line):
1439 return FormatDisasmLine(self.entry, self.heap, line)
1442 class V8Heap(object):
1444 "SYMBOL_TYPE": SeqString,
1445 "ASCII_SYMBOL_TYPE": SeqString,
1446 "CONS_SYMBOL_TYPE": ConsString,
1447 "CONS_ASCII_SYMBOL_TYPE": ConsString,
1448 "EXTERNAL_SYMBOL_TYPE": ExternalString,
1449 "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
1450 "EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
1451 "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString,
1452 "SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
1453 "SHORT_EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
1454 "STRING_TYPE": SeqString,
1455 "ASCII_STRING_TYPE": SeqString,
1456 "CONS_STRING_TYPE": ConsString,
1457 "CONS_ASCII_STRING_TYPE": ConsString,
1458 "EXTERNAL_STRING_TYPE": ExternalString,
1459 "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE": ExternalString,
1460 "EXTERNAL_ASCII_STRING_TYPE": ExternalString,
1462 "ODDBALL_TYPE": Oddball,
1463 "FIXED_ARRAY_TYPE": FixedArray,
1464 "JS_FUNCTION_TYPE": JSFunction,
1465 "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
1466 "SCRIPT_TYPE": Script,
1467 "CODE_CACHE_TYPE": CodeCache,
1471 def __init__(self, reader, stack_map):
1472 self.reader = reader
1473 self.stack_map = stack_map
1476 def FindObjectOrSmi(self, tagged_address):
1477 if (tagged_address & 1) == 0: return tagged_address / 2
1478 return self.FindObject(tagged_address)
1480 def FindObject(self, tagged_address):
1481 if tagged_address in self.objects:
1482 return self.objects[tagged_address]
1483 if (tagged_address & self.ObjectAlignmentMask()) != 1: return None
1484 address = tagged_address - 1
1485 if not self.reader.IsValidAddress(address): return None
1486 map_tagged_address = self.reader.ReadUIntPtr(address)
1487 if tagged_address == map_tagged_address:
1489 meta_map = Map(self, None, address)
1490 instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
1491 if instance_type_name != "MAP_TYPE": return None
1492 meta_map.map = meta_map
1495 map = self.FindMap(map_tagged_address)
1496 if map is None: return None
1497 instance_type_name = INSTANCE_TYPES.get(map.instance_type)
1498 if instance_type_name is None: return None
1499 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
1500 object = cls(self, map, address)
1501 self.objects[tagged_address] = object
1504 def FindMap(self, tagged_address):
1505 if (tagged_address & self.MapAlignmentMask()) != 1: return None
1506 address = tagged_address - 1
1507 if not self.reader.IsValidAddress(address): return None
1508 object = Map(self, None, address)
1514 def PointerSize(self):
1515 return self.reader.PointerSize()
1517 def ObjectAlignmentMask(self):
1518 return self.PointerSize() - 1
1520 def MapAlignmentMask(self):
1521 if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64:
1523 elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM:
1525 elif self.reader.arch == MD_CPU_ARCHITECTURE_X86:
1528 def PageAlignmentMask(self):
1529 return (1 << 20) - 1
1532 class KnownObject(HeapObject):
1533 def __init__(self, heap, known_name):
1534 HeapObject.__init__(self, heap, None, None)
1535 self.known_name = known_name
1538 return "<%s>" % self.known_name
1541 class KnownMap(HeapObject):
1542 def __init__(self, heap, known_name, instance_type):
1543 HeapObject.__init__(self, heap, None, None)
1544 self.instance_type = instance_type
1545 self.known_name = known_name
1548 return "<%s>" % self.known_name
1551 class InspectionPadawan(object):
1552 """The padawan can improve annotations by sensing well-known objects."""
1553 def __init__(self, reader, heap):
1554 self.reader = reader
1556 self.known_first_map_page = 0
1557 self.known_first_data_page = 0
1558 self.known_first_pointer_page = 0
1560 def __getattr__(self, name):
1561 """An InspectionPadawan can be used instead of V8Heap, even though
1562 it does not inherit from V8Heap (aka. mixin)."""
1563 return getattr(self.heap, name)
1565 def GetPageOffset(self, tagged_address):
1566 return tagged_address & self.heap.PageAlignmentMask()
1568 def IsInKnownMapSpace(self, tagged_address):
1569 page_address = tagged_address & ~self.heap.PageAlignmentMask()
1570 return page_address == self.known_first_map_page
1572 def IsInKnownOldSpace(self, tagged_address):
1573 page_address = tagged_address & ~self.heap.PageAlignmentMask()
1574 return page_address in [self.known_first_data_page,
1575 self.known_first_pointer_page]
1577 def ContainingKnownOldSpaceName(self, tagged_address):
1578 page_address = tagged_address & ~self.heap.PageAlignmentMask()
1579 if page_address == self.known_first_data_page: return "OLD_DATA_SPACE"
1580 if page_address == self.known_first_pointer_page: return "OLD_POINTER_SPACE"
1583 def SenseObject(self, tagged_address):
1584 if self.IsInKnownOldSpace(tagged_address):
1585 offset = self.GetPageOffset(tagged_address)
1586 lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset)
1587 known_obj_name = KNOWN_OBJECTS.get(lookup_key)
1589 return KnownObject(self, known_obj_name)
1590 if self.IsInKnownMapSpace(tagged_address):
1591 known_map = self.SenseMap(tagged_address)
1594 found_obj = self.heap.FindObject(tagged_address)
1595 if found_obj: return found_obj
1596 address = tagged_address - 1
1597 if self.reader.IsValidAddress(address):
1598 map_tagged_address = self.reader.ReadUIntPtr(address)
1599 map = self.SenseMap(map_tagged_address)
1600 if map is None: return None
1601 instance_type_name = INSTANCE_TYPES.get(map.instance_type)
1602 if instance_type_name is None: return None
1603 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
1604 return cls(self, map, address)
1607 def SenseMap(self, tagged_address):
1608 if self.IsInKnownMapSpace(tagged_address):
1609 offset = self.GetPageOffset(tagged_address)
1610 known_map_info = KNOWN_MAPS.get(offset)
1612 known_map_type, known_map_name = known_map_info
1613 return KnownMap(self, known_map_name, known_map_type)
1614 found_map = self.heap.FindMap(tagged_address)
1615 if found_map: return found_map
1618 def FindObjectOrSmi(self, tagged_address):
1619 """When used as a mixin in place of V8Heap."""
1620 found_obj = self.SenseObject(tagged_address)
1621 if found_obj: return found_obj
1622 if (tagged_address & 1) == 0:
1623 return "Smi(%d)" % (tagged_address / 2)
1625 return "Unknown(%s)" % self.reader.FormatIntPtr(tagged_address)
1627 def FindObject(self, tagged_address):
1628 """When used as a mixin in place of V8Heap."""
1629 raise NotImplementedError
1631 def FindMap(self, tagged_address):
1632 """When used as a mixin in place of V8Heap."""
1633 raise NotImplementedError
1635 def PrintKnowledge(self):
1636 print " known_first_map_page = %s\n"\
1637 " known_first_data_page = %s\n"\
1638 " known_first_pointer_page = %s" % (
1639 self.reader.FormatIntPtr(self.known_first_map_page),
1640 self.reader.FormatIntPtr(self.known_first_data_page),
1641 self.reader.FormatIntPtr(self.known_first_pointer_page))
1644 class InspectionShell(cmd.Cmd):
1645 def __init__(self, reader, heap):
1646 cmd.Cmd.__init__(self)
1647 self.reader = reader
1649 self.padawan = InspectionPadawan(reader, heap)
1650 self.prompt = "(grok) "
1652 def do_da(self, address):
1654 Print ASCII string starting at specified address.
1656 address = int(address, 16)
1658 while self.reader.IsValidAddress(address):
1659 code = self.reader.ReadU8(address)
1666 print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address)
1668 print "%s\n" % string
1670 def do_dd(self, address):
1672 Interpret memory at the given address (if available) as a sequence
1673 of words. Automatic alignment is not performed.
1675 start = int(address, 16)
1676 if (start & self.heap.ObjectAlignmentMask()) != 0:
1677 print "Warning: Dumping un-aligned memory, is this what you had in mind?"
1678 for slot in xrange(start,
1679 start + self.reader.PointerSize() * 10,
1680 self.reader.PointerSize()):
1681 if not self.reader.IsValidAddress(slot):
1682 print "Address is not contained within the minidump!"
1684 maybe_address = self.reader.ReadUIntPtr(slot)
1685 heap_object = self.padawan.SenseObject(maybe_address)
1686 print "%s: %s %s" % (self.reader.FormatIntPtr(slot),
1687 self.reader.FormatIntPtr(maybe_address),
1690 def do_do(self, address):
1692 Interpret memory at the given address as a V8 object. Automatic
1693 alignment makes sure that you can pass tagged as well as un-tagged
1696 address = int(address, 16)
1697 if (address & self.heap.ObjectAlignmentMask()) == 0:
1698 address = address + 1
1699 elif (address & self.heap.ObjectAlignmentMask()) != 1:
1700 print "Address doesn't look like a valid pointer!"
1702 heap_object = self.padawan.SenseObject(address)
1704 heap_object.Print(Printer())
1706 print "Address cannot be interpreted as object!"
1708 def do_do_desc(self, address):
1710 Print a descriptor array in a readable format.
1712 start = int(address, 16)
1713 if ((start & 1) == 1): start = start - 1
1714 DescriptorArray(FixedArray(self.heap, None, start)).Print(Printer())
1716 def do_do_map(self, address):
1718 Print a descriptor array in a readable format.
1720 start = int(address, 16)
1721 if ((start & 1) == 1): start = start - 1
1722 Map(self.heap, None, start).Print(Printer())
1724 def do_do_trans(self, address):
1726 Print a transition array in a readable format.
1728 start = int(address, 16)
1729 if ((start & 1) == 1): start = start - 1
1730 TransitionArray(FixedArray(self.heap, None, start)).Print(Printer())
1732 def do_dp(self, address):
1734 Interpret memory at the given address as being on a V8 heap page
1735 and print information about the page header (if available).
1737 address = int(address, 16)
1738 page_address = address & ~self.heap.PageAlignmentMask()
1739 if self.reader.IsValidAddress(page_address):
1740 raise NotImplementedError
1742 print "Page header is not available!"
1744 def do_k(self, arguments):
1746 Teach V8 heap layout information to the inspector. This increases
1747 the amount of annotations the inspector can produce while dumping
1748 data. The first page of each heap space is of particular interest
1749 because it contains known objects that do not move.
1751 self.padawan.PrintKnowledge()
1753 def do_kd(self, address):
1755 Teach V8 heap layout information to the inspector. Set the first
1756 data-space page by passing any pointer into that page.
1758 address = int(address, 16)
1759 page_address = address & ~self.heap.PageAlignmentMask()
1760 self.padawan.known_first_data_page = page_address
1762 def do_km(self, address):
1764 Teach V8 heap layout information to the inspector. Set the first
1765 map-space page by passing any pointer into that page.
1767 address = int(address, 16)
1768 page_address = address & ~self.heap.PageAlignmentMask()
1769 self.padawan.known_first_map_page = page_address
1771 def do_kp(self, address):
1773 Teach V8 heap layout information to the inspector. Set the first
1774 pointer-space page by passing any pointer into that page.
1776 address = int(address, 16)
1777 page_address = address & ~self.heap.PageAlignmentMask()
1778 self.padawan.known_first_pointer_page = page_address
1780 def do_list(self, smth):
1782 List all available memory regions.
1784 def print_region(reader, start, size, location):
1785 print " %s - %s (%d bytes)" % (reader.FormatIntPtr(start),
1786 reader.FormatIntPtr(start + size),
1788 print "Available memory regions:"
1789 self.reader.ForEachMemoryRegion(print_region)
1791 def do_lm(self, arg):
1793 List details for all loaded modules in the minidump. An argument can
1794 be passed to limit the output to only those modules that contain the
1795 argument as a substring (case insensitive match).
1797 for module in self.reader.module_list.modules:
1799 name = GetModuleName(self.reader, module).lower()
1800 if name.find(arg.lower()) >= 0:
1801 PrintModuleDetails(self.reader, module)
1803 PrintModuleDetails(self.reader, module)
1806 def do_s(self, word):
1808 Search for a given word in available memory regions. The given word
1809 is expanded to full pointer size and searched at aligned as well as
1810 un-aligned memory locations. Use 'sa' to search aligned locations
1816 print "Malformed word, prefix with '0x' to use hexadecimal format."
1818 print "Searching for word %d/0x%s:" % (word, self.reader.FormatIntPtr(word))
1819 self.reader.FindWord(word)
1821 def do_sh(self, none):
1823 Search for the V8 Heap object in all available memory regions. You
1824 might get lucky and find this rare treasure full of invaluable
1827 raise NotImplementedError
1829 def do_u(self, args):
1831 Unassemble memory in the region [address, address + size). If the
1832 size is not specified, a default value of 32 bytes is used.
1833 Synopsis: u 0x<address> 0x<size>
1835 args = args.split(' ')
1836 start = int(args[0], 16)
1837 size = int(args[1], 16) if len(args) > 1 else 0x20
1838 if not self.reader.IsValidAddress(start):
1839 print "Address is not contained within the minidump!"
1841 lines = self.reader.GetDisasmLines(start, size)
1843 print FormatDisasmLine(start, self.heap, line)
1846 def do_EOF(self, none):
1847 raise KeyboardInterrupt
1851 CONTEXT_FOR_ARCH = {
1852 MD_CPU_ARCHITECTURE_AMD64:
1853 ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip',
1854 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'],
1855 MD_CPU_ARCHITECTURE_ARM:
1856 ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
1857 'r10', 'r11', 'r12', 'sp', 'lr', 'pc'],
1858 MD_CPU_ARCHITECTURE_X86:
1859 ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
1862 KNOWN_MODULES = {'chrome.exe', 'chrome.dll'}
1864 def GetVersionString(ms, ls):
1865 return "%d.%d.%d.%d" % (ms >> 16, ms & 0xffff, ls >> 16, ls & 0xffff)
1868 def GetModuleName(reader, module):
1869 name = reader.ReadMinidumpString(module.module_name_rva)
1870 # simplify for path manipulation
1871 name = name.encode('utf-8')
1872 return str(os.path.basename(str(name).replace("\\", "/")))
1875 def PrintModuleDetails(reader, module):
1876 print "%s" % GetModuleName(reader, module)
1877 file_version = GetVersionString(module.version_info.dwFileVersionMS,
1878 module.version_info.dwFileVersionLS)
1879 product_version = GetVersionString(module.version_info.dwProductVersionMS,
1880 module.version_info.dwProductVersionLS)
1881 print " base: %s" % reader.FormatIntPtr(module.base_of_image)
1882 print " end: %s" % reader.FormatIntPtr(module.base_of_image +
1883 module.size_of_image)
1884 print " file version: %s" % file_version
1885 print " product version: %s" % product_version
1886 time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
1887 print " timestamp: %s" % time_date_stamp
1890 def AnalyzeMinidump(options, minidump_name):
1891 reader = MinidumpReader(options, minidump_name)
1893 DebugPrint("========================================")
1894 if reader.exception is None:
1895 print "Minidump has no exception info"
1897 print "Exception info:"
1898 exception_thread = reader.thread_map[reader.exception.thread_id]
1899 print " thread id: %d" % exception_thread.id
1900 print " code: %08X" % reader.exception.exception.code
1902 for r in CONTEXT_FOR_ARCH[reader.arch]:
1903 print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r)))
1904 # TODO(vitalyr): decode eflags.
1905 if reader.arch == MD_CPU_ARCHITECTURE_ARM:
1906 print " cpsr: %s" % bin(reader.exception_context.cpsr)[2:]
1908 print " eflags: %s" % bin(reader.exception_context.eflags)[2:]
1912 for module in reader.module_list.modules:
1913 name = GetModuleName(reader, module)
1914 if name in KNOWN_MODULES:
1915 print " %s at %08X" % (name, module.base_of_image)
1916 reader.TryLoadSymbolsFor(name, module)
1919 stack_top = reader.ExceptionSP()
1920 stack_bottom = exception_thread.stack.start + \
1921 exception_thread.stack.memory.data_size
1922 stack_map = {reader.ExceptionIP(): -1}
1923 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
1924 maybe_address = reader.ReadUIntPtr(slot)
1925 if not maybe_address in stack_map:
1926 stack_map[maybe_address] = slot
1927 heap = V8Heap(reader, stack_map)
1929 print "Disassembly around exception.eip:"
1930 eip_symbol = reader.FindSymbol(reader.ExceptionIP())
1931 if eip_symbol is not None:
1933 disasm_start = reader.ExceptionIP() - EIP_PROXIMITY
1934 disasm_bytes = 2 * EIP_PROXIMITY
1936 full_range = reader.FindRegion(reader.ExceptionIP())
1937 if full_range is not None:
1938 disasm_start = full_range[0]
1939 disasm_bytes = full_range[1]
1941 lines = reader.GetDisasmLines(disasm_start, disasm_bytes)
1944 print FormatDisasmLine(disasm_start, heap, line)
1948 heap = V8Heap(reader, None)
1951 FullDump(reader, heap)
1954 InspectionShell(reader, heap).onecmd(options.command)
1958 InspectionShell(reader, heap).cmdloop("type help to get help")
1959 except KeyboardInterrupt:
1961 elif not options.command:
1962 if reader.exception is not None:
1963 frame_pointer = reader.ExceptionFP()
1964 print "Annotated stack (from exception.esp to bottom):"
1965 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
1966 maybe_address = reader.ReadUIntPtr(slot)
1967 heap_object = heap.FindObject(maybe_address)
1968 maybe_symbol = reader.FindSymbol(maybe_address)
1969 if slot == frame_pointer:
1970 maybe_symbol = "<---- frame pointer"
1971 frame_pointer = maybe_address
1972 print "%s: %s %s" % (reader.FormatIntPtr(slot),
1973 reader.FormatIntPtr(maybe_address),
1976 heap_object.Print(Printer())
1982 if __name__ == "__main__":
1983 parser = optparse.OptionParser(USAGE)
1984 parser.add_option("-s", "--shell", dest="shell", action="store_true",
1985 help="start an interactive inspector shell")
1986 parser.add_option("-c", "--command", dest="command", default="",
1987 help="run an interactive inspector shell command and exit")
1988 parser.add_option("-f", "--full", dest="full", action="store_true",
1989 help="dump all information contained in the minidump")
1990 parser.add_option("--symdir", dest="symdir", default=".",
1991 help="directory containing *.pdb.sym file with symbols")
1992 parser.add_option("--objdump",
1993 default="/usr/bin/objdump",
1994 help="objdump tool to use [default: %default]")
1995 options, args = parser.parse_args()
1996 if os.path.exists(options.objdump):
1997 disasm.OBJDUMP_BIN = options.objdump
1998 OBJDUMP_BIN = options.objdump
2000 print "Cannot find %s, falling back to default objdump" % options.objdump
2004 AnalyzeMinidump(options, args[0])