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.
52 USAGE="""usage: %prog [OPTIONS] [DUMP-FILE]
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 "=>".
62 $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp"""
73 class Descriptor(object):
74 """Descriptor of a structure in a memory."""
76 def __init__(self, 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
83 if not self.is_flexible:
84 self.ctype = Descriptor._GetCtype(fields)
85 self.size = ctypes.sizeof(self.ctype)
87 def Read(self, memory, offset):
89 fields_copy = self.fields[:]
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)
97 fields_copy[last] = (name, type)
101 complete_ctype = Descriptor._GetCtype(fields_copy[:last])
103 complete_ctype = self.ctype
104 return complete_ctype.from_buffer(memory, offset)
107 def _GetCtype(fields):
108 class Raw(ctypes.Structure):
113 return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field))
114 for field, _ in Raw._fields_) + "}"
118 def FullDump(reader, heap):
119 """Dump all available memory regions."""
120 def dump_region(reader, start, size, location):
122 while start & 3 != 0:
126 is_executable = reader.IsProbableExecutableRegion(location, size)
127 is_ascii = reader.IsProbableASCIIRegion(location, size)
129 if is_executable is not False:
130 lines = reader.GetDisasmLines(start, size)
132 print FormatDisasmLine(start, heap, line)
135 if is_ascii is not False:
136 # Output in the same format as the Unix hd command
138 for slot in xrange(location, location + size, 16):
141 for i in xrange(0, 16):
142 if slot + i < location + size:
143 byte = ctypes.c_uint8.from_buffer(reader.minidump, slot + i).value
144 if byte >= 0x20 and byte < 0x7f:
145 asc_line += chr(byte)
148 hex_line += " %02x" % (byte)
153 print "%s %s |%s|" % (reader.FormatIntPtr(addr),
158 if is_executable is not True and is_ascii is not True:
159 print "%s - %s" % (reader.FormatIntPtr(start),
160 reader.FormatIntPtr(start + size))
161 for slot in xrange(start,
163 reader.PointerSize()):
164 maybe_address = reader.ReadUIntPtr(slot)
165 heap_object = heap.FindObject(maybe_address)
166 print "%s: %s" % (reader.FormatIntPtr(slot),
167 reader.FormatIntPtr(maybe_address))
169 heap_object.Print(Printer())
172 reader.ForEachMemoryRegion(dump_region)
174 # Heap constants generated by 'make grokdump' in v8heapconst module.
175 INSTANCE_TYPES = v8heapconst.INSTANCE_TYPES
176 KNOWN_MAPS = v8heapconst.KNOWN_MAPS
177 KNOWN_OBJECTS = v8heapconst.KNOWN_OBJECTS
179 # Set of structures and constants that describe the layout of minidump
180 # files. Based on MSDN and Google Breakpad.
182 MINIDUMP_HEADER = Descriptor([
183 ("signature", ctypes.c_uint32),
184 ("version", ctypes.c_uint32),
185 ("stream_count", ctypes.c_uint32),
186 ("stream_directories_rva", ctypes.c_uint32),
187 ("checksum", ctypes.c_uint32),
188 ("time_date_stampt", ctypes.c_uint32),
189 ("flags", ctypes.c_uint64)
192 MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
193 ("data_size", ctypes.c_uint32),
194 ("rva", ctypes.c_uint32)
197 MINIDUMP_STRING = Descriptor([
198 ("length", ctypes.c_uint32),
199 ("buffer", lambda t: ctypes.c_uint8 * (t.length + 2))
202 MINIDUMP_DIRECTORY = Descriptor([
203 ("stream_type", ctypes.c_uint32),
204 ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
207 MD_EXCEPTION_MAXIMUM_PARAMETERS = 15
209 MINIDUMP_EXCEPTION = Descriptor([
210 ("code", ctypes.c_uint32),
211 ("flags", ctypes.c_uint32),
212 ("record", ctypes.c_uint64),
213 ("address", ctypes.c_uint64),
214 ("parameter_count", ctypes.c_uint32),
215 ("unused_alignment", ctypes.c_uint32),
216 ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS)
219 MINIDUMP_EXCEPTION_STREAM = Descriptor([
220 ("thread_id", ctypes.c_uint32),
221 ("unused_alignment", ctypes.c_uint32),
222 ("exception", MINIDUMP_EXCEPTION.ctype),
223 ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
228 MD_RESERVED_STREAM_0 = 1
229 MD_RESERVED_STREAM_1 = 2
230 MD_THREAD_LIST_STREAM = 3
231 MD_MODULE_LIST_STREAM = 4
232 MD_MEMORY_LIST_STREAM = 5
233 MD_EXCEPTION_STREAM = 6
234 MD_SYSTEM_INFO_STREAM = 7
235 MD_THREAD_EX_LIST_STREAM = 8
236 MD_MEMORY_64_LIST_STREAM = 9
237 MD_COMMENT_STREAM_A = 10
238 MD_COMMENT_STREAM_W = 11
239 MD_HANDLE_DATA_STREAM = 12
240 MD_FUNCTION_TABLE_STREAM = 13
241 MD_UNLOADED_MODULE_LIST_STREAM = 14
242 MD_MISC_INFO_STREAM = 15
243 MD_MEMORY_INFO_LIST_STREAM = 16
244 MD_THREAD_INFO_LIST_STREAM = 17
245 MD_HANDLE_OPERATION_LIST_STREAM = 18
247 MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80
249 MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([
250 ("control_word", ctypes.c_uint32),
251 ("status_word", ctypes.c_uint32),
252 ("tag_word", ctypes.c_uint32),
253 ("error_offset", ctypes.c_uint32),
254 ("error_selector", ctypes.c_uint32),
255 ("data_offset", ctypes.c_uint32),
256 ("data_selector", ctypes.c_uint32),
257 ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE),
258 ("cr0_npx_state", ctypes.c_uint32)
261 MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512
264 MD_CONTEXT_X86 = 0x00010000
265 MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001)
266 MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002)
267 MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004)
268 MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008)
269 MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010)
270 MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020)
272 def EnableOnFlag(type, flag):
273 return lambda o: [None, type][int((o.context_flags & flag) != 0)]
275 MINIDUMP_CONTEXT_X86 = Descriptor([
276 ("context_flags", ctypes.c_uint32),
277 # MD_CONTEXT_X86_DEBUG_REGISTERS.
278 ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
279 ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
280 ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
281 ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
282 ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
283 ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
284 # MD_CONTEXT_X86_FLOATING_POINT.
285 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype,
286 MD_CONTEXT_X86_FLOATING_POINT)),
287 # MD_CONTEXT_X86_SEGMENTS.
288 ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
289 ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
290 ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
291 ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
292 # MD_CONTEXT_X86_INTEGER.
293 ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
294 ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
295 ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
296 ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
297 ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
298 ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
299 # MD_CONTEXT_X86_CONTROL.
300 ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
301 ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
302 ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
303 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
304 ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
305 ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
306 # MD_CONTEXT_X86_EXTENDED_REGISTERS.
307 ("extended_registers",
308 EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE,
309 MD_CONTEXT_X86_EXTENDED_REGISTERS))
312 MD_CONTEXT_ARM = 0x40000000
313 MD_CONTEXT_ARM_INTEGER = (MD_CONTEXT_ARM | 0x00000002)
314 MD_CONTEXT_ARM_FLOATING_POINT = (MD_CONTEXT_ARM | 0x00000004)
315 MD_FLOATINGSAVEAREA_ARM_FPR_COUNT = 32
316 MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT = 8
318 MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([
319 ("fpscr", ctypes.c_uint64),
320 ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPR_COUNT),
321 ("extra", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT)
324 MINIDUMP_CONTEXT_ARM = Descriptor([
325 ("context_flags", ctypes.c_uint32),
326 # MD_CONTEXT_ARM_INTEGER.
327 ("r0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
328 ("r1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
329 ("r2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
330 ("r3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
331 ("r4", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
332 ("r5", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
333 ("r6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
334 ("r7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
335 ("r8", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
336 ("r9", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
337 ("r10", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
338 ("r11", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
339 ("r12", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
340 ("sp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
341 ("lr", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
342 ("pc", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
343 ("cpsr", ctypes.c_uint32),
344 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype,
345 MD_CONTEXT_ARM_FLOATING_POINT))
348 MD_CONTEXT_AMD64 = 0x00100000
349 MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001)
350 MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002)
351 MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004)
352 MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008)
353 MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010)
355 MINIDUMP_CONTEXT_AMD64 = Descriptor([
356 ("p1_home", ctypes.c_uint64),
357 ("p2_home", ctypes.c_uint64),
358 ("p3_home", ctypes.c_uint64),
359 ("p4_home", ctypes.c_uint64),
360 ("p5_home", ctypes.c_uint64),
361 ("p6_home", ctypes.c_uint64),
362 ("context_flags", ctypes.c_uint32),
363 ("mx_csr", ctypes.c_uint32),
364 # MD_CONTEXT_AMD64_CONTROL.
365 ("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
366 # MD_CONTEXT_AMD64_SEGMENTS
367 ("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
368 ("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
369 ("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
370 ("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
371 # MD_CONTEXT_AMD64_CONTROL.
372 ("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
373 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)),
374 # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
375 ("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
376 ("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
377 ("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
378 ("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
379 ("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
380 ("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
381 # MD_CONTEXT_AMD64_INTEGER.
382 ("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
383 ("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
384 ("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
385 ("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
386 # MD_CONTEXT_AMD64_CONTROL.
387 ("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
388 # MD_CONTEXT_AMD64_INTEGER.
389 ("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
390 ("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
391 ("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
392 ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
393 ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
394 ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
395 ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
396 ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
397 ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
398 ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
399 ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
400 # MD_CONTEXT_AMD64_CONTROL.
401 ("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
402 # MD_CONTEXT_AMD64_FLOATING_POINT
403 ("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
404 MD_CONTEXT_AMD64_FLOATING_POINT)),
405 ("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
406 MD_CONTEXT_AMD64_FLOATING_POINT)),
407 ("vector_control", EnableOnFlag(ctypes.c_uint64,
408 MD_CONTEXT_AMD64_FLOATING_POINT)),
409 # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
410 ("debug_control", EnableOnFlag(ctypes.c_uint64,
411 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
412 ("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64,
413 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
414 ("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64,
415 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
416 ("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64,
417 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
418 ("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64,
419 MD_CONTEXT_AMD64_DEBUG_REGISTERS))
422 MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
423 ("start", ctypes.c_uint64),
424 ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
427 MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
428 ("start", ctypes.c_uint64),
429 ("size", ctypes.c_uint64)
432 MINIDUMP_MEMORY_LIST = Descriptor([
433 ("range_count", ctypes.c_uint32),
434 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
437 MINIDUMP_MEMORY_LIST64 = Descriptor([
438 ("range_count", ctypes.c_uint64),
439 ("base_rva", ctypes.c_uint64),
440 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count)
443 MINIDUMP_THREAD = Descriptor([
444 ("id", ctypes.c_uint32),
445 ("suspend_count", ctypes.c_uint32),
446 ("priority_class", ctypes.c_uint32),
447 ("priority", ctypes.c_uint32),
448 ("ted", ctypes.c_uint64),
449 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype),
450 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
453 MINIDUMP_THREAD_LIST = Descriptor([
454 ("thread_count", ctypes.c_uint32),
455 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
458 MINIDUMP_VS_FIXEDFILEINFO = Descriptor([
459 ("dwSignature", ctypes.c_uint32),
460 ("dwStrucVersion", ctypes.c_uint32),
461 ("dwFileVersionMS", ctypes.c_uint32),
462 ("dwFileVersionLS", ctypes.c_uint32),
463 ("dwProductVersionMS", ctypes.c_uint32),
464 ("dwProductVersionLS", ctypes.c_uint32),
465 ("dwFileFlagsMask", ctypes.c_uint32),
466 ("dwFileFlags", ctypes.c_uint32),
467 ("dwFileOS", ctypes.c_uint32),
468 ("dwFileType", ctypes.c_uint32),
469 ("dwFileSubtype", ctypes.c_uint32),
470 ("dwFileDateMS", ctypes.c_uint32),
471 ("dwFileDateLS", ctypes.c_uint32)
474 MINIDUMP_RAW_MODULE = Descriptor([
475 ("base_of_image", ctypes.c_uint64),
476 ("size_of_image", ctypes.c_uint32),
477 ("checksum", ctypes.c_uint32),
478 ("time_date_stamp", ctypes.c_uint32),
479 ("module_name_rva", ctypes.c_uint32),
480 ("version_info", MINIDUMP_VS_FIXEDFILEINFO.ctype),
481 ("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
482 ("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
483 ("reserved0", ctypes.c_uint32 * 2),
484 ("reserved1", ctypes.c_uint32 * 2)
487 MINIDUMP_MODULE_LIST = Descriptor([
488 ("number_of_modules", ctypes.c_uint32),
489 ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules)
492 MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
493 ("processor_architecture", ctypes.c_uint16)
496 MD_CPU_ARCHITECTURE_X86 = 0
497 MD_CPU_ARCHITECTURE_ARM = 5
498 MD_CPU_ARCHITECTURE_AMD64 = 9
501 def __init__(self, start, size, name):
503 self.end = self.start + size
506 def __cmp__(self, other):
507 if isinstance(other, FuncSymbol):
508 return self.start - other.start
509 return self.start - other
511 def Covers(self, addr):
512 return (self.start <= addr) and (addr < self.end)
514 class MinidumpReader(object):
515 """Minidump (.dmp) reader."""
517 _HEADER_MAGIC = 0x504d444d
519 def __init__(self, options, minidump_name):
520 self.minidump_name = minidump_name
521 self.minidump_file = open(minidump_name, "r")
522 self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE)
523 self.header = MINIDUMP_HEADER.Read(self.minidump, 0)
524 if self.header.signature != MinidumpReader._HEADER_MAGIC:
525 print >>sys.stderr, "Warning: Unsupported minidump header magic!"
526 DebugPrint(self.header)
528 offset = self.header.stream_directories_rva
529 for _ in xrange(self.header.stream_count):
530 directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
531 offset += MINIDUMP_DIRECTORY.size
533 self.exception = None
534 self.exception_context = None
535 self.memory_list = None
536 self.memory_list64 = None
537 self.module_list = None
540 self.symdir = options.symdir
541 self.modules_with_symbols = []
544 # Find MDRawSystemInfo stream and determine arch.
545 for d in directories:
546 if d.stream_type == MD_SYSTEM_INFO_STREAM:
547 system_info = MINIDUMP_RAW_SYSTEM_INFO.Read(
548 self.minidump, d.location.rva)
549 self.arch = system_info.processor_architecture
550 assert self.arch in [MD_CPU_ARCHITECTURE_AMD64,
551 MD_CPU_ARCHITECTURE_ARM,
552 MD_CPU_ARCHITECTURE_X86]
553 assert not self.arch is None
555 for d in directories:
557 if d.stream_type == MD_EXCEPTION_STREAM:
558 self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
559 self.minidump, d.location.rva)
560 DebugPrint(self.exception)
561 if self.arch == MD_CPU_ARCHITECTURE_X86:
562 self.exception_context = MINIDUMP_CONTEXT_X86.Read(
563 self.minidump, self.exception.thread_context.rva)
564 elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
565 self.exception_context = MINIDUMP_CONTEXT_AMD64.Read(
566 self.minidump, self.exception.thread_context.rva)
567 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
568 self.exception_context = MINIDUMP_CONTEXT_ARM.Read(
569 self.minidump, self.exception.thread_context.rva)
570 DebugPrint(self.exception_context)
571 elif d.stream_type == MD_THREAD_LIST_STREAM:
572 thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
573 assert ctypes.sizeof(thread_list) == d.location.data_size
574 DebugPrint(thread_list)
575 for thread in thread_list.threads:
577 self.thread_map[thread.id] = thread
578 elif d.stream_type == MD_MODULE_LIST_STREAM:
579 assert self.module_list is None
580 self.module_list = MINIDUMP_MODULE_LIST.Read(
581 self.minidump, d.location.rva)
582 assert ctypes.sizeof(self.module_list) == d.location.data_size
583 elif d.stream_type == MD_MEMORY_LIST_STREAM:
584 print >>sys.stderr, "Warning: This is not a full minidump!"
585 assert self.memory_list is None
586 self.memory_list = MINIDUMP_MEMORY_LIST.Read(
587 self.minidump, d.location.rva)
588 assert ctypes.sizeof(self.memory_list) == d.location.data_size
589 DebugPrint(self.memory_list)
590 elif d.stream_type == MD_MEMORY_64_LIST_STREAM:
591 assert self.memory_list64 is None
592 self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read(
593 self.minidump, d.location.rva)
594 assert ctypes.sizeof(self.memory_list64) == d.location.data_size
595 DebugPrint(self.memory_list64)
597 def IsValidAddress(self, address):
598 return self.FindLocation(address) is not None
600 def ReadU8(self, address):
601 location = self.FindLocation(address)
602 return ctypes.c_uint8.from_buffer(self.minidump, location).value
604 def ReadU32(self, address):
605 location = self.FindLocation(address)
606 return ctypes.c_uint32.from_buffer(self.minidump, location).value
608 def ReadU64(self, address):
609 location = self.FindLocation(address)
610 return ctypes.c_uint64.from_buffer(self.minidump, location).value
612 def ReadUIntPtr(self, address):
613 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
614 return self.ReadU64(address)
615 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
616 return self.ReadU32(address)
617 elif self.arch == MD_CPU_ARCHITECTURE_X86:
618 return self.ReadU32(address)
620 def ReadBytes(self, address, size):
621 location = self.FindLocation(address)
622 return self.minidump[location:location + size]
624 def _ReadWord(self, location):
625 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
626 return ctypes.c_uint64.from_buffer(self.minidump, location).value
627 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
628 return ctypes.c_uint32.from_buffer(self.minidump, location).value
629 elif self.arch == MD_CPU_ARCHITECTURE_X86:
630 return ctypes.c_uint32.from_buffer(self.minidump, location).value
632 def IsProbableASCIIRegion(self, location, length):
635 for loc in xrange(location, location + length):
636 byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
639 if byte < 0x20 and byte != 0:
641 if byte < 0x7f and byte >= 0x20:
643 if byte == 0xa: # newline
645 if ascii_bytes * 10 <= length:
647 if length > 0 and ascii_bytes > non_ascii_bytes * 7:
649 if ascii_bytes > non_ascii_bytes * 3:
653 def IsProbableExecutableRegion(self, location, length):
655 sixty_four = self.arch == MD_CPU_ARCHITECTURE_AMD64
656 for loc in xrange(location, location + length):
657 byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
658 if (byte == 0x8b or # mov
659 byte == 0x89 or # mov reg-reg
660 (byte & 0xf0) == 0x50 or # push/pop
661 (sixty_four and (byte & 0xf0) == 0x40) or # rex prefix
662 byte == 0xc3 or # return
663 byte == 0x74 or # jeq
664 byte == 0x84 or # jeq far
665 byte == 0x75 or # jne
666 byte == 0x85 or # jne far
667 byte == 0xe8 or # call
668 byte == 0xe9 or # jmp far
669 byte == 0xeb): # jmp near
671 opcode_percent = (opcode_bytes * 100) / length
673 if opcode_percent > threshold + 2:
675 if opcode_percent > threshold - 2:
679 def FindRegion(self, addr):
681 def is_in(reader, start, size, location):
682 if addr >= start and addr < start + size:
685 self.ForEachMemoryRegion(is_in)
690 def ForEachMemoryRegion(self, cb):
691 if self.memory_list64 is not None:
692 for r in self.memory_list64.ranges:
693 location = self.memory_list64.base_rva + offset
694 cb(self, r.start, r.size, location)
697 if self.memory_list is not None:
698 for r in self.memory_list.ranges:
699 cb(self, r.start, r.memory.data_size, r.memory.rva)
701 def FindWord(self, word, alignment=0):
702 def search_inside_region(reader, start, size, location):
703 location = (location + alignment) & ~alignment
704 for loc in xrange(location, location + size - self.PointerSize()):
705 if reader._ReadWord(loc) == word:
706 slot = start + (loc - location)
707 print "%s: %s" % (reader.FormatIntPtr(slot),
708 reader.FormatIntPtr(word))
709 self.ForEachMemoryRegion(search_inside_region)
711 def FindWordList(self, word):
714 def search_inside_region(reader, start, size, location):
715 for loc in xrange(location, location + size - self.PointerSize()):
716 if reader._ReadWord(loc) == word:
717 slot = start + (loc - location)
718 if slot % self.PointerSize() == 0:
719 aligned_res.append(slot)
721 unaligned_res.append(slot)
722 self.ForEachMemoryRegion(search_inside_region)
723 return (aligned_res, unaligned_res)
725 def FindLocation(self, address):
727 if self.memory_list64 is not None:
728 for r in self.memory_list64.ranges:
729 if r.start <= address < r.start + r.size:
730 return self.memory_list64.base_rva + offset + address - r.start
732 if self.memory_list is not None:
733 for r in self.memory_list.ranges:
734 if r.start <= address < r.start + r.memory.data_size:
735 return r.memory.rva + address - r.start
738 def GetDisasmLines(self, address, size):
739 def CountUndefinedInstructions(lines):
740 pattern = "<UNDEFINED>"
741 return sum([line.count(pattern) for (ignore, line) in lines])
743 location = self.FindLocation(address)
744 if location is None: return []
746 possible_objdump_flags = [""]
747 if self.arch == MD_CPU_ARCHITECTURE_X86:
749 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
751 possible_objdump_flags = ["", "--disassembler-options=force-thumb"]
752 elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
754 results = [ disasm.GetDisasmLines(self.minidump_name,
760 for objdump_flags in possible_objdump_flags ]
761 return min(results, key=CountUndefinedInstructions)
765 self.minidump.close()
766 self.minidump_file.close()
768 def ExceptionIP(self):
769 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
770 return self.exception_context.rip
771 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
772 return self.exception_context.pc
773 elif self.arch == MD_CPU_ARCHITECTURE_X86:
774 return self.exception_context.eip
776 def ExceptionSP(self):
777 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
778 return self.exception_context.rsp
779 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
780 return self.exception_context.sp
781 elif self.arch == MD_CPU_ARCHITECTURE_X86:
782 return self.exception_context.esp
784 def ExceptionFP(self):
785 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
786 return self.exception_context.rbp
787 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
789 elif self.arch == MD_CPU_ARCHITECTURE_X86:
790 return self.exception_context.ebp
792 def FormatIntPtr(self, value):
793 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
794 return "%016x" % value
795 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
796 return "%08x" % value
797 elif self.arch == MD_CPU_ARCHITECTURE_X86:
798 return "%08x" % value
800 def PointerSize(self):
801 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
803 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
805 elif self.arch == MD_CPU_ARCHITECTURE_X86:
808 def Register(self, name):
809 return self.exception_context.__getattribute__(name)
811 def ReadMinidumpString(self, rva):
812 string = bytearray(MINIDUMP_STRING.Read(self.minidump, rva).buffer)
813 string = string.decode("utf16")
814 return string[0:len(string) - 1]
816 # Load FUNC records from a BreakPad symbol file
818 # http://code.google.com/p/google-breakpad/wiki/SymbolFiles
820 def _LoadSymbolsFrom(self, symfile, baseaddr):
821 print "Loading symbols from %s" % (symfile)
823 with open(symfile) as f:
826 r"^FUNC ([a-f0-9]+) ([a-f0-9]+) ([a-f0-9]+) (.*)$", line)
827 if result is not None:
828 start = int(result.group(1), 16)
829 size = int(result.group(2), 16)
830 name = result.group(4).rstrip()
831 bisect.insort_left(self.symbols,
832 FuncSymbol(baseaddr + start, size, name))
835 def TryLoadSymbolsFor(self, modulename, module):
837 symfile = os.path.join(self.symdir,
838 modulename.replace('.', '_') + ".pdb.sym")
839 if os.path.isfile(symfile):
840 self._LoadSymbolsFrom(symfile, module.base_of_image)
841 self.modules_with_symbols.append(module)
842 except Exception as e:
843 print " ... failure (%s)" % (e)
845 # Returns true if address is covered by some module that has loaded symbols.
846 def _IsInModuleWithSymbols(self, addr):
847 for module in self.modules_with_symbols:
848 start = module.base_of_image
849 end = start + module.size_of_image
850 if (start <= addr) and (addr < end):
854 # Find symbol covering the given address and return its name in format
855 # <symbol name>+<offset from the start>
856 def FindSymbol(self, addr):
857 if not self._IsInModuleWithSymbols(addr):
860 i = bisect.bisect_left(self.symbols, addr)
862 if (0 < i) and self.symbols[i - 1].Covers(addr):
863 symbol = self.symbols[i - 1]
864 elif (i < len(self.symbols)) and self.symbols[i].Covers(addr):
865 symbol = self.symbols[i]
868 diff = addr - symbol.start
869 return "%s+0x%x" % (symbol.name, diff)
872 class Printer(object):
873 """Printer with indentation support."""
884 def Print(self, string):
885 print "%s%s" % (self._IndentString(), string)
887 def PrintLines(self, lines):
888 indent = self._IndentString()
889 print "\n".join("%s%s" % (indent, line) for line in lines)
891 def _IndentString(self):
892 return self.indent * " "
895 ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
898 def FormatDisasmLine(start, heap, line):
899 line_address = start + line[0]
900 stack_slot = heap.stack_map.get(line_address)
904 code = AnnotateAddresses(heap, line[1])
906 # Compute the actual call target which the disassembler is too stupid
907 # to figure out (it adds the call offset to the disassembly offset rather
908 # than the absolute instruction address).
909 if heap.reader.arch == MD_CPU_ARCHITECTURE_X86:
910 if code.startswith("e8"):
912 if len(words) > 6 and words[5] == "call":
913 offset = int(words[4] + words[3] + words[2] + words[1], 16)
914 target = (line_address + offset + 5) & 0xFFFFFFFF
915 code = code.replace(words[6], "0x%08x" % target)
916 # TODO(jkummerow): port this hack to ARM and x64.
918 return "%s%08x %08x: %s" % (marker, line_address, line[0], code)
921 def AnnotateAddresses(heap, line):
923 for m in ADDRESS_RE.finditer(line):
924 maybe_address = int(m.group(0), 16)
925 object = heap.FindObject(maybe_address)
926 if not object: continue
927 extra.append(str(object))
928 if len(extra) == 0: return line
929 return "%s ;; %s" % (line, ", ".join(extra))
932 class HeapObject(object):
933 def __init__(self, heap, map, address):
936 self.address = address
939 return isinstance(self, cls)
945 return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address),
946 INSTANCE_TYPES[self.map.instance_type])
948 def ObjectField(self, offset):
949 field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
950 return self.heap.FindObjectOrSmi(field_value)
952 def SmiField(self, offset):
953 field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
954 if (field_value & 1) == 0:
955 return field_value / 2
959 class Map(HeapObject):
960 def Decode(self, offset, size, value):
961 return (value >> offset) & ((1 << size) - 1)
964 def InstanceSizesOffset(self):
965 return self.heap.PointerSize()
967 def InstanceSizeOffset(self):
968 return self.InstanceSizesOffset()
970 def InObjectProperties(self):
971 return self.InstanceSizeOffset() + 1
973 def PreAllocatedPropertyFields(self):
974 return self.InObjectProperties() + 1
977 return self.PreAllocatedPropertyFields() + 1
979 # Instance Attributes
980 def InstanceAttributesOffset(self):
981 return self.InstanceSizesOffset() + self.heap.IntSize()
983 def InstanceTypeOffset(self):
984 return self.InstanceAttributesOffset()
986 def UnusedPropertyFieldsOffset(self):
987 return self.InstanceTypeOffset() + 1
989 def BitFieldOffset(self):
990 return self.UnusedPropertyFieldsOffset() + 1
992 def BitField2Offset(self):
993 return self.BitFieldOffset() + 1
996 def PrototypeOffset(self):
997 return self.InstanceAttributesOffset() + self.heap.IntSize()
999 def ConstructorOffset(self):
1000 return self.PrototypeOffset() + self.heap.PointerSize()
1002 def TransitionsOrBackPointerOffset(self):
1003 return self.ConstructorOffset() + self.heap.PointerSize()
1005 def DescriptorsOffset(self):
1006 return self.TransitionsOrBackPointerOffset() + self.heap.PointerSize()
1008 def CodeCacheOffset(self):
1009 return self.DescriptorsOffset() + self.heap.PointerSize()
1011 def DependentCodeOffset(self):
1012 return self.CodeCacheOffset() + self.heap.PointerSize()
1014 def BitField3Offset(self):
1015 return self.DependentCodeOffset() + self.heap.PointerSize()
1017 def ReadByte(self, offset):
1018 return self.heap.reader.ReadU8(self.address + offset)
1021 p.Print("Map(%08x)" % (self.address))
1022 p.Print("- size: %d, inobject: %d, preallocated: %d, visitor: %d" % (
1023 self.ReadByte(self.InstanceSizeOffset()),
1024 self.ReadByte(self.InObjectProperties()),
1025 self.ReadByte(self.PreAllocatedPropertyFields()),
1028 bitfield = self.ReadByte(self.BitFieldOffset())
1029 bitfield2 = self.ReadByte(self.BitField2Offset())
1030 p.Print("- %s, unused: %d, bf: %d, bf2: %d" % (
1031 INSTANCE_TYPES[self.ReadByte(self.InstanceTypeOffset())],
1032 self.ReadByte(self.UnusedPropertyFieldsOffset()),
1033 bitfield, bitfield2))
1035 p.Print("- kind: %s" % (self.Decode(3, 5, bitfield2)))
1037 bitfield3 = self.ObjectField(self.BitField3Offset())
1039 "- EnumLength: %d NumberOfOwnDescriptors: %d OwnsDescriptors: %s" % (
1040 self.Decode(0, 11, bitfield3),
1041 self.Decode(11, 11, bitfield3),
1042 self.Decode(25, 1, bitfield3)))
1043 p.Print("- IsShared: %s" % (self.Decode(22, 1, bitfield3)))
1044 p.Print("- FunctionWithPrototype: %s" % (self.Decode(23, 1, bitfield3)))
1045 p.Print("- DictionaryMap: %s" % (self.Decode(24, 1, bitfield3)))
1047 descriptors = self.ObjectField(self.DescriptorsOffset())
1048 if descriptors.__class__ == FixedArray:
1049 DescriptorArray(descriptors).Print(p)
1051 p.Print("Descriptors: %s" % (descriptors))
1053 transitions = self.ObjectField(self.TransitionsOrBackPointerOffset())
1054 if transitions.__class__ == FixedArray:
1055 TransitionArray(transitions).Print(p)
1057 p.Print("TransitionsOrBackPointer: %s" % (transitions))
1059 def __init__(self, heap, map, address):
1060 HeapObject.__init__(self, heap, map, address)
1061 self.instance_type = \
1062 heap.reader.ReadU8(self.address + self.InstanceTypeOffset())
1065 class String(HeapObject):
1066 def LengthOffset(self):
1067 # First word after the map is the hash, the second is the length.
1068 return self.heap.PointerSize() * 2
1070 def __init__(self, heap, map, address):
1071 HeapObject.__init__(self, heap, map, address)
1072 self.length = self.SmiField(self.LengthOffset())
1081 return "\"%s\"" % self.GetChars()
1084 class SeqString(String):
1085 def CharsOffset(self):
1086 return self.heap.PointerSize() * 3
1088 def __init__(self, heap, map, address):
1089 String.__init__(self, heap, map, address)
1090 self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(),
1097 class ExternalString(String):
1098 # TODO(vegorov) fix ExternalString for X64 architecture
1099 RESOURCE_OFFSET = 12
1101 WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
1102 WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
1104 def __init__(self, heap, map, address):
1105 String.__init__(self, heap, map, address)
1106 reader = heap.reader
1108 reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
1109 self.chars = "?external string?"
1110 if not reader.IsValidAddress(self.resource): return
1111 string_impl_address = self.resource + \
1112 ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
1113 if not reader.IsValidAddress(string_impl_address): return
1114 string_impl = reader.ReadU32(string_impl_address)
1115 chars_ptr_address = string_impl + \
1116 ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
1117 if not reader.IsValidAddress(chars_ptr_address): return
1118 chars_ptr = reader.ReadU32(chars_ptr_address)
1119 if not reader.IsValidAddress(chars_ptr): return
1120 raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
1121 self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
1127 class ConsString(String):
1128 def LeftOffset(self):
1129 return self.heap.PointerSize() * 3
1131 def RightOffset(self):
1132 return self.heap.PointerSize() * 4
1134 def __init__(self, heap, map, address):
1135 String.__init__(self, heap, map, address)
1136 self.left = self.ObjectField(self.LeftOffset())
1137 self.right = self.ObjectField(self.RightOffset())
1141 return self.left.GetChars() + self.right.GetChars()
1143 return "***CAUGHT EXCEPTION IN GROKDUMP***"
1146 class Oddball(HeapObject):
1147 # Should match declarations in objects.h
1158 def ToStringOffset(self):
1159 return self.heap.PointerSize()
1161 def ToNumberOffset(self):
1162 return self.ToStringOffset() + self.heap.PointerSize()
1164 def KindOffset(self):
1165 return self.ToNumberOffset() + self.heap.PointerSize()
1167 def __init__(self, heap, map, address):
1168 HeapObject.__init__(self, heap, map, address)
1169 self.to_string = self.ObjectField(self.ToStringOffset())
1170 self.kind = self.SmiField(self.KindOffset())
1177 return "Oddball(%08x, <%s>)" % (self.address, str(self.to_string))
1180 if 0 <= self.kind < len(Oddball.KINDS):
1181 kind = Oddball.KINDS[self.kind]
1182 return "Oddball(%08x, kind=%s)" % (self.address, kind)
1185 class FixedArray(HeapObject):
1186 def LengthOffset(self):
1187 return self.heap.PointerSize()
1189 def ElementsOffset(self):
1190 return self.heap.PointerSize() * 2
1192 def MemberOffset(self, i):
1193 return self.ElementsOffset() + self.heap.PointerSize() * i
1196 return self.ObjectField(self.MemberOffset(i))
1198 def __init__(self, heap, map, address):
1199 HeapObject.__init__(self, heap, map, address)
1200 self.length = self.SmiField(self.LengthOffset())
1203 p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1205 p.Print("length: %d" % self.length)
1206 base_offset = self.ElementsOffset()
1207 for i in xrange(self.length):
1208 offset = base_offset + 4 * i
1210 p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
1220 return "FixedArray(%08x, length=%d)" % (self.address, self.length)
1223 class DescriptorArray(object):
1224 def __init__(self, array):
1228 return self.array.Get(0)
1230 def Decode(self, offset, size, value):
1231 return (value >> offset) & ((1 << size) - 1)
1240 def Type(self, value):
1241 return DescriptorArray.TYPES[self.Decode(0, 3, value)]
1243 def Attributes(self, value):
1244 attributes = self.Decode(3, 3, value)
1246 if (attributes & 0): result += ["ReadOnly"]
1247 if (attributes & 1): result += ["DontEnum"]
1248 if (attributes & 2): result += ["DontDelete"]
1249 return "[" + (",".join(result)) + "]"
1251 def Deleted(self, value):
1252 return self.Decode(6, 1, value) == 1
1254 def FieldIndex(self, value):
1255 return self.Decode(20, 11, value)
1257 def Pointer(self, value):
1258 return self.Decode(6, 11, value)
1260 def Details(self, di, value):
1264 self.Attributes(value),
1265 self.FieldIndex(value),
1271 length = self.Length()
1274 p.Print("Descriptors(%08x, length=%d)" % (array.address, length))
1275 p.Print("[et] %s" % (array.Get(1)))
1277 for di in xrange(length):
1279 p.Print("0x%x" % (array.address + array.MemberOffset(i)))
1280 p.Print("[%i] name: %s" % (di, array.Get(i + 0)))
1281 p.Print("[%i] details: %s %s field-index %i pointer %i" % \
1282 self.Details(di, array.Get(i + 1)))
1283 p.Print("[%i] value: %s" % (di, array.Get(i + 2)))
1285 end = self.array.length // 3
1287 p.Print("[%i-%i] slack descriptors" % (length, end))
1290 class TransitionArray(object):
1291 def __init__(self, array):
1294 def IsSimpleTransition(self):
1295 return self.array.length <= 2
1298 # SimpleTransition cases
1299 if self.IsSimpleTransition():
1300 return self.array.length - 1
1301 return (self.array.length - 3) // 2
1304 length = self.Length()
1307 p.Print("Transitions(%08x, length=%d)" % (array.address, length))
1308 p.Print("[backpointer] %s" % (array.Get(0)))
1309 if self.IsSimpleTransition():
1311 p.Print("[simple target] %s" % (array.Get(1)))
1314 elements = array.Get(1)
1315 if elements is not None:
1316 p.Print("[elements ] %s" % (elements))
1318 prototype = array.Get(2)
1319 if prototype is not None:
1320 p.Print("[prototype ] %s" % (prototype))
1322 for di in xrange(length):
1324 p.Print("[%i] symbol: %s" % (di, array.Get(i + 0)))
1325 p.Print("[%i] target: %s" % (di, array.Get(i + 1)))
1328 class JSFunction(HeapObject):
1329 def CodeEntryOffset(self):
1330 return 3 * self.heap.PointerSize()
1332 def SharedOffset(self):
1333 return 5 * self.heap.PointerSize()
1335 def __init__(self, heap, map, address):
1336 HeapObject.__init__(self, heap, map, address)
1338 heap.reader.ReadU32(self.address + self.CodeEntryOffset())
1339 self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1)
1340 self.shared = self.ObjectField(self.SharedOffset())
1343 source = "\n".join(" %s" % line for line in self._GetSource().split("\n"))
1344 p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1346 p.Print("inferred name: %s" % self.shared.inferred_name)
1347 if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
1348 p.Print("script name: %s" % self.shared.script.name)
1350 p.PrintLines(self._GetSource().split("\n"))
1353 if self.code != self.shared.code:
1354 p.Print("unoptimized code:")
1355 self.shared.code.Print(p)
1361 if self.shared.Is(SharedFunctionInfo):
1362 inferred_name = self.shared.inferred_name
1363 return "JSFunction(%s, %s)" % \
1364 (self.heap.reader.FormatIntPtr(self.address), inferred_name)
1366 def _GetSource(self):
1368 start = self.shared.start_position
1369 end = self.shared.end_position
1370 if not self.shared.script.Is(Script): return source
1371 script_source = self.shared.script.source
1372 if not script_source.Is(String): return source
1374 source = script_source.GetChars()[start:end]
1378 class SharedFunctionInfo(HeapObject):
1379 def CodeOffset(self):
1380 return 2 * self.heap.PointerSize()
1382 def ScriptOffset(self):
1383 return 7 * self.heap.PointerSize()
1385 def InferredNameOffset(self):
1386 return 9 * self.heap.PointerSize()
1388 def EndPositionOffset(self):
1389 return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
1391 def StartPositionAndTypeOffset(self):
1392 return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
1394 def __init__(self, heap, map, address):
1395 HeapObject.__init__(self, heap, map, address)
1396 self.code = self.ObjectField(self.CodeOffset())
1397 self.script = self.ObjectField(self.ScriptOffset())
1398 self.inferred_name = self.ObjectField(self.InferredNameOffset())
1399 if heap.PointerSize() == 8:
1400 start_position_and_type = \
1401 heap.reader.ReadU32(self.StartPositionAndTypeOffset())
1402 self.start_position = start_position_and_type >> 2
1403 pseudo_smi_end_position = \
1404 heap.reader.ReadU32(self.EndPositionOffset())
1405 self.end_position = pseudo_smi_end_position >> 2
1407 start_position_and_type = \
1408 self.SmiField(self.StartPositionAndTypeOffset())
1409 if start_position_and_type:
1410 self.start_position = start_position_and_type >> 2
1412 self.start_position = None
1413 self.end_position = \
1414 self.SmiField(self.EndPositionOffset())
1417 class Script(HeapObject):
1418 def SourceOffset(self):
1419 return self.heap.PointerSize()
1421 def NameOffset(self):
1422 return self.SourceOffset() + self.heap.PointerSize()
1424 def __init__(self, heap, map, address):
1425 HeapObject.__init__(self, heap, map, address)
1426 self.source = self.ObjectField(self.SourceOffset())
1427 self.name = self.ObjectField(self.NameOffset())
1430 class CodeCache(HeapObject):
1431 def DefaultCacheOffset(self):
1432 return self.heap.PointerSize()
1434 def NormalTypeCacheOffset(self):
1435 return self.DefaultCacheOffset() + self.heap.PointerSize()
1437 def __init__(self, heap, map, address):
1438 HeapObject.__init__(self, heap, map, address)
1439 self.default_cache = self.ObjectField(self.DefaultCacheOffset())
1440 self.normal_type_cache = self.ObjectField(self.NormalTypeCacheOffset())
1443 p.Print("CodeCache(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1445 p.Print("default cache: %s" % self.default_cache)
1446 p.Print("normal type cache: %s" % self.normal_type_cache)
1451 class Code(HeapObject):
1452 CODE_ALIGNMENT_MASK = (1 << 5) - 1
1454 def InstructionSizeOffset(self):
1455 return self.heap.PointerSize()
1458 def HeaderSize(heap):
1459 return (heap.PointerSize() + heap.IntSize() + \
1460 4 * heap.PointerSize() + 3 * heap.IntSize() + \
1461 Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK
1463 def __init__(self, heap, map, address):
1464 HeapObject.__init__(self, heap, map, address)
1465 self.entry = self.address + Code.HeaderSize(heap)
1466 self.instruction_size = \
1467 heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
1470 lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
1471 p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1473 p.Print("instruction_size: %d" % self.instruction_size)
1474 p.PrintLines(self._FormatLine(line) for line in lines)
1478 def _FormatLine(self, line):
1479 return FormatDisasmLine(self.entry, self.heap, line)
1482 class V8Heap(object):
1484 "SYMBOL_TYPE": SeqString,
1485 "ASCII_SYMBOL_TYPE": SeqString,
1486 "CONS_SYMBOL_TYPE": ConsString,
1487 "CONS_ASCII_SYMBOL_TYPE": ConsString,
1488 "EXTERNAL_SYMBOL_TYPE": ExternalString,
1489 "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
1490 "EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
1491 "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString,
1492 "SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
1493 "SHORT_EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
1494 "STRING_TYPE": SeqString,
1495 "ASCII_STRING_TYPE": SeqString,
1496 "CONS_STRING_TYPE": ConsString,
1497 "CONS_ASCII_STRING_TYPE": ConsString,
1498 "EXTERNAL_STRING_TYPE": ExternalString,
1499 "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE": ExternalString,
1500 "EXTERNAL_ASCII_STRING_TYPE": ExternalString,
1502 "ODDBALL_TYPE": Oddball,
1503 "FIXED_ARRAY_TYPE": FixedArray,
1504 "JS_FUNCTION_TYPE": JSFunction,
1505 "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
1506 "SCRIPT_TYPE": Script,
1507 "CODE_CACHE_TYPE": CodeCache,
1511 def __init__(self, reader, stack_map):
1512 self.reader = reader
1513 self.stack_map = stack_map
1516 def FindObjectOrSmi(self, tagged_address):
1517 if (tagged_address & 1) == 0: return tagged_address / 2
1518 return self.FindObject(tagged_address)
1520 def FindObject(self, tagged_address):
1521 if tagged_address in self.objects:
1522 return self.objects[tagged_address]
1523 if (tagged_address & self.ObjectAlignmentMask()) != 1: return None
1524 address = tagged_address - 1
1525 if not self.reader.IsValidAddress(address): return None
1526 map_tagged_address = self.reader.ReadUIntPtr(address)
1527 if tagged_address == map_tagged_address:
1529 meta_map = Map(self, None, address)
1530 instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
1531 if instance_type_name != "MAP_TYPE": return None
1532 meta_map.map = meta_map
1535 map = self.FindMap(map_tagged_address)
1536 if map is None: return None
1537 instance_type_name = INSTANCE_TYPES.get(map.instance_type)
1538 if instance_type_name is None: return None
1539 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
1540 object = cls(self, map, address)
1541 self.objects[tagged_address] = object
1544 def FindMap(self, tagged_address):
1545 if (tagged_address & self.MapAlignmentMask()) != 1: return None
1546 address = tagged_address - 1
1547 if not self.reader.IsValidAddress(address): return None
1548 object = Map(self, None, address)
1554 def PointerSize(self):
1555 return self.reader.PointerSize()
1557 def ObjectAlignmentMask(self):
1558 return self.PointerSize() - 1
1560 def MapAlignmentMask(self):
1561 if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64:
1563 elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM:
1565 elif self.reader.arch == MD_CPU_ARCHITECTURE_X86:
1568 def PageAlignmentMask(self):
1569 return (1 << 20) - 1
1572 class KnownObject(HeapObject):
1573 def __init__(self, heap, known_name):
1574 HeapObject.__init__(self, heap, None, None)
1575 self.known_name = known_name
1578 return "<%s>" % self.known_name
1581 class KnownMap(HeapObject):
1582 def __init__(self, heap, known_name, instance_type):
1583 HeapObject.__init__(self, heap, None, None)
1584 self.instance_type = instance_type
1585 self.known_name = known_name
1588 return "<%s>" % self.known_name
1591 COMMENT_RE = re.compile(r"^C (0x[0-9a-fA-F]+) (.*)$")
1592 PAGEADDRESS_RE = re.compile(
1593 r"^P (mappage|pointerpage|datapage) (0x[0-9a-fA-F]+)$")
1596 class InspectionInfo(object):
1597 def __init__(self, minidump_name, reader):
1598 self.comment_file = minidump_name + ".comments"
1599 self.address_comments = {}
1600 self.page_address = {}
1601 if os.path.exists(self.comment_file):
1602 with open(self.comment_file, "r") as f:
1603 lines = f.readlines()
1607 m = COMMENT_RE.match(l)
1609 self.address_comments[int(m.group(1), 0)] = m.group(2)
1610 m = PAGEADDRESS_RE.match(l)
1612 self.page_address[m.group(1)] = int(m.group(2), 0)
1613 self.reader = reader
1615 self.color_addresses()
1618 def get_page_address(self, page_kind):
1619 return self.page_address.get(page_kind, 0)
1621 def save_page_address(self, page_kind, address):
1622 with open(self.comment_file, "a") as f:
1623 f.write("P %s 0x%x\n" % (page_kind, address))
1626 def color_addresses(self):
1627 # Color all stack addresses.
1628 exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
1629 stack_top = self.reader.ExceptionSP()
1630 stack_bottom = exception_thread.stack.start + \
1631 exception_thread.stack.memory.data_size
1632 frame_pointer = self.reader.ExceptionFP()
1633 self.styles[frame_pointer] = "frame"
1634 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
1635 self.styles[slot] = "stackaddress"
1636 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
1637 maybe_address = self.reader.ReadUIntPtr(slot)
1638 self.styles[maybe_address] = "stackval"
1639 if slot == frame_pointer:
1640 self.styles[slot] = "frame"
1641 frame_pointer = maybe_address
1642 self.styles[self.reader.ExceptionIP()] = "pc"
1644 def get_style_class(self, address):
1645 return self.styles.get(address, None)
1647 def get_style_class_string(self, address):
1648 style = self.get_style_class(address)
1650 return " class=\"%s\" " % style
1654 def set_comment(self, address, comment):
1655 self.address_comments[address] = comment
1656 with open(self.comment_file, "a") as f:
1657 f.write("C 0x%x %s\n" % (address, comment))
1660 def get_comment(self, address):
1661 return self.address_comments.get(address, "")
1664 class InspectionPadawan(object):
1665 """The padawan can improve annotations by sensing well-known objects."""
1666 def __init__(self, reader, heap):
1667 self.reader = reader
1669 self.known_first_map_page = 0
1670 self.known_first_data_page = 0
1671 self.known_first_pointer_page = 0
1673 def __getattr__(self, name):
1674 """An InspectionPadawan can be used instead of V8Heap, even though
1675 it does not inherit from V8Heap (aka. mixin)."""
1676 return getattr(self.heap, name)
1678 def GetPageOffset(self, tagged_address):
1679 return tagged_address & self.heap.PageAlignmentMask()
1681 def IsInKnownMapSpace(self, tagged_address):
1682 page_address = tagged_address & ~self.heap.PageAlignmentMask()
1683 return page_address == self.known_first_map_page
1685 def IsInKnownOldSpace(self, tagged_address):
1686 page_address = tagged_address & ~self.heap.PageAlignmentMask()
1687 return page_address in [self.known_first_data_page,
1688 self.known_first_pointer_page]
1690 def ContainingKnownOldSpaceName(self, tagged_address):
1691 page_address = tagged_address & ~self.heap.PageAlignmentMask()
1692 if page_address == self.known_first_data_page: return "OLD_DATA_SPACE"
1693 if page_address == self.known_first_pointer_page: return "OLD_POINTER_SPACE"
1696 def SenseObject(self, tagged_address):
1697 if self.IsInKnownOldSpace(tagged_address):
1698 offset = self.GetPageOffset(tagged_address)
1699 lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset)
1700 known_obj_name = KNOWN_OBJECTS.get(lookup_key)
1702 return KnownObject(self, known_obj_name)
1703 if self.IsInKnownMapSpace(tagged_address):
1704 known_map = self.SenseMap(tagged_address)
1707 found_obj = self.heap.FindObject(tagged_address)
1708 if found_obj: return found_obj
1709 address = tagged_address - 1
1710 if self.reader.IsValidAddress(address):
1711 map_tagged_address = self.reader.ReadUIntPtr(address)
1712 map = self.SenseMap(map_tagged_address)
1713 if map is None: return None
1714 instance_type_name = INSTANCE_TYPES.get(map.instance_type)
1715 if instance_type_name is None: return None
1716 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
1717 return cls(self, map, address)
1720 def SenseMap(self, tagged_address):
1721 if self.IsInKnownMapSpace(tagged_address):
1722 offset = self.GetPageOffset(tagged_address)
1723 known_map_info = KNOWN_MAPS.get(offset)
1725 known_map_type, known_map_name = known_map_info
1726 return KnownMap(self, known_map_name, known_map_type)
1727 found_map = self.heap.FindMap(tagged_address)
1728 if found_map: return found_map
1731 def FindObjectOrSmi(self, tagged_address):
1732 """When used as a mixin in place of V8Heap."""
1733 found_obj = self.SenseObject(tagged_address)
1734 if found_obj: return found_obj
1735 if (tagged_address & 1) == 0:
1736 return "Smi(%d)" % (tagged_address / 2)
1738 return "Unknown(%s)" % self.reader.FormatIntPtr(tagged_address)
1740 def FindObject(self, tagged_address):
1741 """When used as a mixin in place of V8Heap."""
1742 raise NotImplementedError
1744 def FindMap(self, tagged_address):
1745 """When used as a mixin in place of V8Heap."""
1746 raise NotImplementedError
1748 def PrintKnowledge(self):
1749 print " known_first_map_page = %s\n"\
1750 " known_first_data_page = %s\n"\
1751 " known_first_pointer_page = %s" % (
1752 self.reader.FormatIntPtr(self.known_first_map_page),
1753 self.reader.FormatIntPtr(self.known_first_data_page),
1754 self.reader.FormatIntPtr(self.known_first_pointer_page))
1760 <meta content="text/html; charset=utf-8" http-equiv="content-type">
1761 <style media="screen" type="text/css">
1764 font-family: monospace;
1768 border-collapse : collapse;
1769 border-spacing : 0px;
1773 border-collapse : collapse;
1774 border-spacing : 0px;
1782 padding-right : 1em;
1789 .header .navigation {
1798 background-color : yellow;
1802 background-color : magenta;
1805 tr.inexact-highlight-line {
1806 background-color : pink;
1810 background-color: inherit;
1811 border: 1px solid LightGray;
1815 border : 1px solid LightGray;
1820 padding:0 15px 0 15px;
1824 background-color : cyan;
1828 background-color : LightGray;
1832 background-color : LightCyan;
1836 background-color : cyan;
1845 text-decoration : none;
1850 text-decoration : none;
1867 <script type="application/javascript">
1869 var address_str = "address-";
1870 var address_len = address_str.length;
1872 function comment() {
1873 var s = event.srcElement.id;
1874 var index = s.indexOf(address_str);
1876 send_comment(s.substring(index + address_len), event.srcElement.value);
1880 function send_comment(address, comment) {
1881 xmlhttp = new XMLHttpRequest();
1882 address = encodeURIComponent(address)
1883 comment = encodeURIComponent(comment)
1885 "setcomment?%(query_dump)s&address=" + address +
1886 "&comment=" + comment, true);
1890 var dump_str = "dump-";
1891 var dump_len = dump_str.length;
1893 function dump_comment() {
1894 var s = event.srcElement.id;
1895 var index = s.indexOf(dump_str);
1897 send_dump_desc(s.substring(index + dump_len), event.srcElement.value);
1901 function send_dump_desc(name, desc) {
1902 xmlhttp = new XMLHttpRequest();
1903 name = encodeURIComponent(name)
1904 desc = encodeURIComponent(desc)
1906 "setdumpdesc?dump=" + name +
1907 "&description=" + desc, true);
1911 function onpage(kind, address) {
1912 xmlhttp = new XMLHttpRequest();
1913 kind = encodeURIComponent(kind)
1914 address = encodeURIComponent(address)
1915 xmlhttp.onreadystatechange = function() {
1916 if (xmlhttp.readyState==4 && xmlhttp.status==200) {
1917 location.reload(true)
1921 "setpageaddress?%(query_dump)s&kind=" + kind +
1922 "&address=" + address);
1928 <title>Dump %(dump_name)s</title>
1932 <div class="header">
1933 <form class="navigation" action="search.html">
1934 <a href="summary.html?%(query_dump)s">Context info</a>
1935 <a href="info.html?%(query_dump)s">Dump info</a>
1936 <a href="modules.html?%(query_dump)s">Modules</a>
1938 <input type="search" name="val">
1939 <input type="submit" name="search" value="Search">
1940 <input type="hidden" name="dump" value="%(dump_name)s">
1942 <form class="navigation" action="disasm.html#highlight">
1946 <input type="search" name="val">
1947 <input type="submit" name="disasm" value="Disasm">
1951 <a href="dumps.html">Dumps...</a>
1965 class WebParameterError(Exception):
1966 def __init__(self, message):
1967 Exception.__init__(self, message)
1970 class InspectionWebHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1971 def formatter(self, query_components):
1972 name = query_components.get("dump", [None])[0]
1973 return self.server.get_dump_formatter(name)
1975 def send_success_html_headers(self):
1976 self.send_response(200)
1977 self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
1978 self.send_header("Pragma", "no-cache")
1979 self.send_header("Expires", "0")
1980 self.send_header('Content-type','text/html')
1986 parsedurl = urlparse.urlparse(self.path)
1987 query_components = urlparse.parse_qs(parsedurl.query)
1988 if parsedurl.path == "/dumps.html":
1989 self.send_success_html_headers()
1990 self.server.output_dumps(self.wfile)
1991 elif parsedurl.path == "/summary.html":
1992 self.send_success_html_headers()
1993 self.formatter(query_components).output_summary(self.wfile)
1994 elif parsedurl.path == "/info.html":
1995 self.send_success_html_headers()
1996 self.formatter(query_components).output_info(self.wfile)
1997 elif parsedurl.path == "/modules.html":
1998 self.send_success_html_headers()
1999 self.formatter(query_components).output_modules(self.wfile)
2000 elif parsedurl.path == "/search.html":
2001 address = query_components.get("val", [])
2002 if len(address) != 1:
2003 self.send_error(404, "Invalid params")
2005 self.send_success_html_headers()
2006 self.formatter(query_components).output_search_res(
2007 self.wfile, address[0])
2008 elif parsedurl.path == "/disasm.html":
2009 address = query_components.get("val", [])
2010 exact = query_components.get("exact", ["on"])
2011 if len(address) != 1:
2012 self.send_error(404, "Invalid params")
2014 self.send_success_html_headers()
2015 self.formatter(query_components).output_disasm(
2016 self.wfile, address[0], exact[0])
2017 elif parsedurl.path == "/data.html":
2018 address = query_components.get("val", [])
2019 datakind = query_components.get("type", ["address"])
2020 if len(address) == 1 and len(datakind) == 1:
2021 self.send_success_html_headers()
2022 self.formatter(query_components).output_data(
2023 self.wfile, address[0], datakind[0])
2025 self.send_error(404,'Invalid params')
2026 elif parsedurl.path == "/setdumpdesc":
2027 name = query_components.get("dump", [""])
2028 description = query_components.get("description", [""])
2029 if len(name) == 1 and len(description) == 1:
2031 description = description[0]
2032 if self.server.set_dump_desc(name, description):
2033 self.send_success_html_headers()
2034 self.wfile.write("OK")
2036 self.send_error(404,'Invalid params')
2037 elif parsedurl.path == "/setcomment":
2038 address = query_components.get("address", [])
2039 comment = query_components.get("comment", [""])
2040 if len(address) == 1 and len(comment) == 1:
2041 address = address[0]
2042 comment = comment[0]
2043 self.formatter(query_components).set_comment(address, comment)
2044 self.send_success_html_headers()
2045 self.wfile.write("OK")
2047 self.send_error(404,'Invalid params')
2048 elif parsedurl.path == "/setpageaddress":
2049 kind = query_components.get("kind", [])
2050 address = query_components.get("address", [""])
2051 if len(kind) == 1 and len(address) == 1:
2053 address = address[0]
2054 self.formatter(query_components).set_page_address(kind, address)
2055 self.send_success_html_headers()
2056 self.wfile.write("OK")
2058 self.send_error(404,'Invalid params')
2060 self.send_error(404,'File Not Found: %s' % self.path)
2063 self.send_error(404,'File Not Found: %s' % self.path)
2065 except WebParameterError as e:
2066 self.send_error(404, 'Web parameter error: %s' % e.message)
2069 HTML_REG_FORMAT = "<span class=\"register\"><b>%s</b>: %s</span>\n"
2072 class InspectionWebFormatter(object):
2076 def __init__(self, switches, minidump_name, http_server):
2077 self.dumpfilename = os.path.split(minidump_name)[1]
2078 self.encfilename = urllib.urlencode({ 'dump' : self.dumpfilename })
2079 self.reader = MinidumpReader(switches, minidump_name)
2080 self.server = http_server
2083 exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
2084 stack_top = self.reader.ExceptionSP()
2085 stack_bottom = exception_thread.stack.start + \
2086 exception_thread.stack.memory.data_size
2087 stack_map = {self.reader.ExceptionIP(): -1}
2088 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
2089 maybe_address = self.reader.ReadUIntPtr(slot)
2090 if not maybe_address in stack_map:
2091 stack_map[maybe_address] = slot
2092 self.heap = V8Heap(self.reader, stack_map)
2094 self.padawan = InspectionPadawan(self.reader, self.heap)
2095 self.comments = InspectionInfo(minidump_name, self.reader)
2096 self.padawan.known_first_data_page = (
2097 self.comments.get_page_address("datapage"))
2098 self.padawan.known_first_map_page = (
2099 self.comments.get_page_address("mappage"))
2100 self.padawan.known_first_pointer_page = (
2101 self.comments.get_page_address("pointerpage"))
2103 def set_comment(self, straddress, comment):
2105 address = int(straddress, 0)
2106 self.comments.set_comment(address, comment)
2108 print "Invalid address"
2110 def set_page_address(self, kind, straddress):
2112 address = int(straddress, 0)
2113 if kind == "datapage":
2114 self.padawan.known_first_data_page = address
2115 elif kind == "mappage":
2116 self.padawan.known_first_map_page = address
2117 elif kind == "pointerpage":
2118 self.padawan.known_first_pointer_page = address
2119 self.comments.save_page_address(kind, address)
2121 print "Invalid address"
2123 def td_from_address(self, f, address):
2124 f.write("<td %s>" % self.comments.get_style_class_string(address))
2126 def format_address(self, maybeaddress, straddress = None):
2127 if maybeaddress is None:
2128 return "not in dump"
2130 if straddress is None:
2131 straddress = "0x" + self.reader.FormatIntPtr(maybeaddress)
2133 if not self.reader.IsValidAddress(maybeaddress):
2134 style_class = " class=\"nodump\""
2135 return ("<a %s href=\"search.html?%s&val=%s\">%s</a>" %
2136 (style_class, self.encfilename, straddress, straddress))
2138 def output_header(self, f):
2139 f.write(WEB_HEADER %
2140 { "query_dump" : self.encfilename,
2141 "dump_name" : cgi.escape(self.dumpfilename) })
2143 def output_footer(self, f):
2146 MAX_CONTEXT_STACK = 4096
2148 def output_summary(self, f):
2149 self.output_header(f)
2150 f.write('<div class="code">')
2151 self.output_context(f, InspectionWebFormatter.CONTEXT_SHORT)
2152 self.output_disasm_pc(f)
2155 exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
2156 stack_bottom = exception_thread.stack.start + \
2157 min(exception_thread.stack.memory.data_size, self.MAX_CONTEXT_STACK)
2158 stack_top = self.reader.ExceptionSP()
2159 self.output_words(f, stack_top - 16, stack_bottom, stack_top, "Stack")
2162 self.output_footer(f)
2165 def output_info(self, f):
2166 self.output_header(f)
2167 f.write("<h3>Dump info</h3>\n")
2168 f.write("Description: ")
2169 self.server.output_dump_desc_field(f, self.dumpfilename)
2171 f.write("Filename: ")
2172 f.write("<span class=\"code\">%s</span><br>\n" % (self.dumpfilename))
2173 dt = datetime.datetime.fromtimestamp(self.reader.header.time_date_stampt)
2174 f.write("Timestamp: %s<br>\n" % dt.strftime('%Y-%m-%d %H:%M:%S'))
2175 self.output_context(f, InspectionWebFormatter.CONTEXT_FULL)
2176 self.output_address_ranges(f)
2177 self.output_footer(f)
2180 def output_address_ranges(self, f):
2182 def print_region(_reader, start, size, _location):
2183 regions[start] = size
2184 self.reader.ForEachMemoryRegion(print_region)
2185 f.write("<h3>Available memory regions</h3>\n")
2186 f.write('<div class="code">')
2187 f.write("<table class=\"regions\">\n")
2188 f.write("<thead><tr>")
2189 f.write("<th>Start address</th>")
2190 f.write("<th>End address</th>")
2191 f.write("<th>Number of bytes</th>")
2192 f.write("</tr></thead>\n")
2193 for start in sorted(regions):
2194 size = regions[start]
2196 f.write("<td>%s</td>" % self.format_address(start))
2197 f.write("<td> %s</td>" % self.format_address(start + size))
2198 f.write("<td> %d</td>" % size)
2200 f.write("</table>\n")
2204 def output_module_details(self, f, module):
2205 f.write("<b>%s</b>" % GetModuleName(self.reader, module))
2206 file_version = GetVersionString(module.version_info.dwFileVersionMS,
2207 module.version_info.dwFileVersionLS)
2208 product_version = GetVersionString(module.version_info.dwProductVersionMS,
2209 module.version_info.dwProductVersionLS)
2210 f.write("<br> \n")
2211 f.write("base: %s" % self.reader.FormatIntPtr(module.base_of_image))
2212 f.write("<br> \n")
2213 f.write(" end: %s" % self.reader.FormatIntPtr(module.base_of_image +
2214 module.size_of_image))
2215 f.write("<br> \n")
2216 f.write(" file version: %s" % file_version)
2217 f.write("<br> \n")
2218 f.write(" product version: %s" % product_version)
2219 f.write("<br> \n")
2220 time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
2221 f.write(" timestamp: %s" % time_date_stamp)
2224 def output_modules(self, f):
2225 self.output_header(f)
2226 f.write('<div class="code">')
2227 for module in self.reader.module_list.modules:
2228 self.output_module_details(f, module)
2230 self.output_footer(f)
2233 def output_context(self, f, details):
2234 exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
2235 f.write("<h3>Exception context</h3>")
2236 f.write('<div class="code">\n')
2237 f.write("Thread id: %d" % exception_thread.id)
2238 f.write(" Exception code: %08X\n" %
2239 self.reader.exception.exception.code)
2240 if details == InspectionWebFormatter.CONTEXT_FULL:
2241 if self.reader.exception.exception.parameter_count > 0:
2242 f.write(" Exception parameters: \n")
2243 for i in xrange(0, self.reader.exception.exception.parameter_count):
2244 f.write("%08x" % self.reader.exception.exception.information[i])
2245 f.write("<br><br>\n")
2247 for r in CONTEXT_FOR_ARCH[self.reader.arch]:
2248 f.write(HTML_REG_FORMAT %
2249 (r, self.format_address(self.reader.Register(r))))
2250 # TODO(vitalyr): decode eflags.
2251 if self.reader.arch == MD_CPU_ARCHITECTURE_ARM:
2252 f.write("<b>cpsr</b>: %s" % bin(self.reader.exception_context.cpsr)[2:])
2254 f.write("<b>eflags</b>: %s" %
2255 bin(self.reader.exception_context.eflags)[2:])
2259 def align_down(self, a, size):
2260 alignment_correction = a % size
2261 return a - alignment_correction
2263 def align_up(self, a, size):
2264 alignment_correction = (size - 1) - ((a + size - 1) % size)
2265 return a + alignment_correction
2267 def format_object(self, address):
2268 heap_object = self.padawan.SenseObject(address)
2269 return cgi.escape(str(heap_object or ""))
2271 def output_data(self, f, straddress, datakind):
2273 self.output_header(f)
2274 address = int(straddress, 0)
2275 if not self.reader.IsValidAddress(address):
2276 f.write("<h3>Address 0x%x not found in the dump.</h3>" % address)
2278 region = self.reader.FindRegion(address)
2279 if datakind == "address":
2280 self.output_words(f, region[0], region[0] + region[1], address, "Dump")
2281 elif datakind == "ascii":
2282 self.output_ascii(f, region[0], region[0] + region[1], address)
2283 self.output_footer(f)
2286 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
2289 def output_words(self, f, start_address, end_address,
2290 highlight_address, desc):
2291 region = self.reader.FindRegion(highlight_address)
2293 f.write("<h3>Address 0x%x not found in the dump.</h3>\n" %
2294 (highlight_address))
2296 size = self.heap.PointerSize()
2297 start_address = self.align_down(start_address, size)
2298 low = self.align_down(region[0], size)
2299 high = self.align_up(region[0] + region[1], size)
2300 if start_address < low:
2302 end_address = self.align_up(end_address, size)
2303 if end_address > high:
2307 if start_address != low or end_address != high:
2308 expand = ("(<a href=\"data.html?%s&val=0x%x#highlight\">"
2311 (self.encfilename, highlight_address))
2313 f.write("<h3>%s 0x%x - 0x%x, "
2314 "highlighting <a href=\"#highlight\">0x%x</a> %s</h3>\n" %
2315 (desc, start_address, end_address, highlight_address, expand))
2316 f.write('<div class="code">')
2317 f.write("<table class=\"codedump\">\n")
2319 for slot in xrange(start_address, end_address, size):
2321 maybe_address = None
2322 end_region = region[0] + region[1]
2323 if slot < region[0] or slot + size > end_region:
2325 for i in xrange(end_region, slot + size):
2328 xrange(max(slot, region[0]), min(slot + size, end_region))):
2329 straddress += "%02x" % self.reader.ReadU8(i)
2330 for i in xrange(slot, region[0]):
2333 maybe_address = self.reader.ReadUIntPtr(slot)
2334 straddress = self.format_address(maybe_address)
2336 heap_object = self.format_object(maybe_address)
2338 address_fmt = "%s </td>\n"
2339 if slot == highlight_address:
2340 f.write("<tr class=\"highlight-line\">\n")
2341 address_fmt = "<a id=\"highlight\"></a>%s </td>\n"
2342 elif slot < highlight_address and highlight_address < slot + size:
2343 f.write("<tr class=\"inexact-highlight-line\">\n")
2344 address_fmt = "<a id=\"highlight\"></a>%s </td>\n"
2349 self.output_comment_box(f, "da-", slot)
2352 self.td_from_address(f, slot)
2353 f.write(address_fmt % self.format_address(slot))
2355 self.td_from_address(f, maybe_address)
2356 f.write(": %s </td>\n" % straddress)
2358 if maybe_address != None:
2359 self.output_comment_box(
2360 f, "sv-" + self.reader.FormatIntPtr(slot), maybe_address)
2362 f.write(" <td>%s</td>\n" % (heap_object or ''))
2364 f.write("</table>\n")
2368 def output_ascii(self, f, start_address, end_address, highlight_address):
2369 region = self.reader.FindRegion(highlight_address)
2371 f.write("<h3>Address %x not found in the dump.</h3>" %
2374 if start_address < region[0]:
2375 start_address = region[0]
2376 if end_address > region[0] + region[1]:
2377 end_address = region[0] + region[1]
2380 if start_address != region[0] or end_address != region[0] + region[1]:
2381 link = ("data.html?%s&val=0x%x&type=ascii#highlight" %
2382 (self.encfilename, highlight_address))
2383 expand = "(<a href=\"%s\">more...</a>)" % link
2385 f.write("<h3>ASCII dump 0x%x - 0x%x, highlighting 0x%x %s</h3>" %
2386 (start_address, end_address, highlight_address, expand))
2390 f.write('<div class="code">')
2392 start = self.align_down(start_address, line_width)
2394 for address in xrange(start, end_address):
2395 if address % 64 == 0:
2396 if address != start:
2398 f.write("0x%08x: " % address)
2399 if address < start_address:
2402 if address == highlight_address:
2403 f.write("<span class=\"highlight\">")
2404 code = self.reader.ReadU8(address)
2405 if code < 127 and code >= 32:
2411 if address == highlight_address:
2416 def output_disasm(self, f, straddress, strexact):
2418 self.output_header(f)
2419 address = int(straddress, 0)
2420 if not self.reader.IsValidAddress(address):
2421 f.write("<h3>Address 0x%x not found in the dump.</h3>" % address)
2423 region = self.reader.FindRegion(address)
2424 self.output_disasm_range(
2425 f, region[0], region[0] + region[1], address, strexact == "on")
2426 self.output_footer(f)
2428 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
2431 def output_disasm_range(
2432 self, f, start_address, end_address, highlight_address, exact):
2433 region = self.reader.FindRegion(highlight_address)
2434 if start_address < region[0]:
2435 start_address = region[0]
2436 if end_address > region[0] + region[1]:
2437 end_address = region[0] + region[1]
2438 count = end_address - start_address
2439 lines = self.reader.GetDisasmLines(start_address, count)
2443 if line[0] + start_address == highlight_address:
2447 start_address = highlight_address
2448 count = end_address - start_address
2449 lines = self.reader.GetDisasmLines(highlight_address, count)
2451 if start_address != region[0] or end_address != region[0] + region[1]:
2453 if exact and not found and end_address == region[0] + region[1]:
2454 exactness = "&exact=off"
2455 expand = ("(<a href=\"disasm.html?%s%s"
2456 "&val=0x%x#highlight\">more...</a>)" %
2457 (self.encfilename, exactness, highlight_address))
2459 f.write("<h3>Disassembling 0x%x - 0x%x, highlighting 0x%x %s</h3>" %
2460 (start_address, end_address, highlight_address, expand))
2461 f.write('<div class="code">')
2462 f.write("<table class=\"codedump\">\n");
2463 for i in xrange(0, len(lines)):
2465 next_address = count
2466 if i + 1 < len(lines):
2467 next_line = lines[i + 1]
2468 next_address = next_line[0]
2469 self.format_disasm_line(
2470 f, start_address, line, next_address, highlight_address)
2471 f.write("</table>\n")
2475 def annotate_disasm_addresses(self, line):
2477 for m in ADDRESS_RE.finditer(line):
2478 maybe_address = int(m.group(0), 16)
2479 formatted_address = self.format_address(maybe_address, m.group(0))
2480 line = line.replace(m.group(0), formatted_address)
2481 object_info = self.padawan.SenseObject(maybe_address)
2484 extra.append(cgi.escape(str(object_info)))
2487 return ("%s <span class=\"disasmcomment\">;; %s</span>" %
2488 (line, ", ".join(extra)))
2490 def format_disasm_line(
2491 self, f, start, line, next_address, highlight_address):
2492 line_address = start + line[0]
2493 address_fmt = " <td>%s</td>\n"
2494 if line_address == highlight_address:
2495 f.write("<tr class=\"highlight-line\">\n")
2496 address_fmt = " <td><a id=\"highlight\">%s</a></td>\n"
2497 elif (line_address < highlight_address and
2498 highlight_address < next_address + start):
2499 f.write("<tr class=\"inexact-highlight-line\">\n")
2500 address_fmt = " <td><a id=\"highlight\">%s</a></td>\n"
2503 num_bytes = next_address - line[0]
2504 stack_slot = self.heap.stack_map.get(line_address)
2508 op_offset = 3 * num_bytes - 1
2511 # Compute the actual call target which the disassembler is too stupid
2512 # to figure out (it adds the call offset to the disassembly offset rather
2513 # than the absolute instruction address).
2514 if self.heap.reader.arch == MD_CPU_ARCHITECTURE_X86:
2515 if code.startswith("e8"):
2516 words = code.split()
2517 if len(words) > 6 and words[5] == "call":
2518 offset = int(words[4] + words[3] + words[2] + words[1], 16)
2519 target = (line_address + offset + 5) & 0xFFFFFFFF
2520 code = code.replace(words[6], "0x%08x" % target)
2521 # TODO(jkummerow): port this hack to ARM and x64.
2523 opcodes = code[:op_offset]
2524 code = self.annotate_disasm_addresses(code[op_offset:])
2526 self.output_comment_box(f, "codel-", line_address)
2528 f.write(address_fmt % marker)
2530 self.td_from_address(f, line_address)
2531 f.write("%s (+0x%x)</td>\n" %
2532 (self.format_address(line_address), line[0]))
2533 f.write(" <td>: %s </td>\n" % opcodes)
2534 f.write(" <td>%s</td>\n" % code)
2537 def output_comment_box(self, f, prefix, address):
2538 f.write("<input type=\"text\" class=\"commentinput\" "
2539 "id=\"%s-address-0x%s\" onchange=\"comment()\" value=\"%s\">" %
2541 self.reader.FormatIntPtr(address),
2542 cgi.escape(self.comments.get_comment(address)) or ""))
2544 MAX_FOUND_RESULTS = 100
2546 def output_find_results(self, f, results):
2547 f.write("Addresses")
2548 toomany = len(results) > self.MAX_FOUND_RESULTS
2550 f.write("(found %i results, displaying only first %i)" %
2551 (len(results), self.MAX_FOUND_RESULTS))
2553 results = sorted(results)
2554 results = results[:min(len(results), self.MAX_FOUND_RESULTS)]
2555 for address in results:
2556 f.write("<span %s>%s</span>\n" %
2557 (self.comments.get_style_class_string(address),
2558 self.format_address(address)))
2563 def output_page_info(self, f, page_kind, page_address, my_page_address):
2564 if my_page_address == page_address and page_address != 0:
2565 f.write("Marked first %s page.\n" % page_kind)
2567 f.write("<span id=\"%spage\" style=\"display:none\">" % page_kind)
2568 f.write("Marked first %s page." % page_kind)
2569 f.write("</span>\n")
2570 f.write("<button onclick=\"onpage('%spage', '0x%x')\">" %
2571 (page_kind, my_page_address))
2572 f.write("Mark as first %s page</button>\n" % page_kind)
2575 def output_search_res(self, f, straddress):
2577 self.output_header(f)
2578 f.write("<h3>Search results for %s</h3>" % straddress)
2580 address = int(straddress, 0)
2582 f.write("Comment: ")
2583 self.output_comment_box(f, "search-", address)
2586 page_address = address & ~self.heap.PageAlignmentMask()
2588 f.write("Page info: \n")
2589 self.output_page_info(f, "data", self.padawan.known_first_data_page, \
2591 self.output_page_info(f, "map", self.padawan.known_first_map_page, \
2593 self.output_page_info(f, "pointer", \
2594 self.padawan.known_first_pointer_page, \
2597 if not self.reader.IsValidAddress(address):
2598 f.write("<h3>The contents at address %s not found in the dump.</h3>" % \
2602 self.output_words(f, address - 8, address + 32, address, "Dump")
2606 self.output_ascii(f, address, address + 256, address)
2610 self.output_disasm_range(f, address - 16, address + 16, address, True)
2612 aligned_res, unaligned_res = self.reader.FindWordList(address)
2614 if len(aligned_res) > 0:
2615 f.write("<h3>Occurrences of 0x%x at aligned addresses</h3>\n" %
2617 self.output_find_results(f, aligned_res)
2619 if len(unaligned_res) > 0:
2620 f.write("<h3>Occurrences of 0x%x at unaligned addresses</h3>\n" % \
2622 self.output_find_results(f, unaligned_res)
2624 if len(aligned_res) + len(unaligned_res) == 0:
2625 f.write("<h3>No occurences of 0x%x found in the dump</h3>\n" % address)
2627 self.output_footer(f)
2630 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
2633 def output_disasm_pc(self, f):
2634 address = self.reader.ExceptionIP()
2635 if not self.reader.IsValidAddress(address):
2637 self.output_disasm_range(f, address - 16, address + 16, address, True)
2640 WEB_DUMPS_HEADER = """
2644 <meta content="text/html; charset=utf-8" http-equiv="content-type">
2645 <style media="screen" type="text/css">
2648 border-collapse : collapse;
2649 border-spacing : 0px;
2650 font-family: monospace;
2654 border : 1px solid LightGray;
2660 <script type="application/javascript">
2662 var dump_str = "dump-";
2663 var dump_len = dump_str.length;
2665 function dump_comment() {
2666 var s = event.srcElement.id;
2667 var index = s.indexOf(dump_str);
2669 send_dump_desc(s.substring(index + dump_len), event.srcElement.value);
2673 function send_dump_desc(name, desc) {
2674 xmlhttp = new XMLHttpRequest();
2675 name = encodeURIComponent(name)
2676 desc = encodeURIComponent(desc)
2678 "setdumpdesc?dump=" + name +
2679 "&description=" + desc, true);
2685 <title>Dump list</title>
2691 WEB_DUMPS_FOOTER = """
2696 DUMP_FILE_RE = re.compile(r"[-_0-9a-zA-Z][-\._0-9a-zA-Z]*\.dmp$")
2699 class InspectionWebServer(BaseHTTPServer.HTTPServer):
2700 def __init__(self, port_number, switches, minidump_name):
2701 BaseHTTPServer.HTTPServer.__init__(
2702 self, ('', port_number), InspectionWebHandler)
2703 splitpath = os.path.split(minidump_name)
2704 self.dumppath = splitpath[0]
2705 self.dumpfilename = splitpath[1]
2706 self.default_formatter = InspectionWebFormatter(
2707 switches, minidump_name, self)
2708 self.formatters = { self.dumpfilename : self.default_formatter }
2709 self.switches = switches
2711 def output_dump_desc_field(self, f, name):
2713 descfile = open(os.path.join(self.dumppath, name + ".desc"), "r")
2714 desc = descfile.readline()
2718 f.write("<input type=\"text\" class=\"dumpcomments\" "
2719 "id=\"dump-%s\" onchange=\"dump_comment()\" value=\"%s\">\n" %
2720 (cgi.escape(name), desc))
2722 def set_dump_desc(self, name, description):
2723 if not DUMP_FILE_RE.match(name):
2725 fname = os.path.join(self.dumppath, name)
2726 if not os.path.isfile(fname):
2728 fname = fname + ".desc"
2729 descfile = open(fname, "w")
2730 descfile.write(description)
2734 def get_dump_formatter(self, name):
2736 return self.default_formatter
2738 if not DUMP_FILE_RE.match(name):
2739 raise WebParameterError("Invalid name '%s'" % name)
2740 formatter = self.formatters.get(name, None)
2741 if formatter is None:
2743 formatter = InspectionWebFormatter(
2744 self.switches, os.path.join(self.dumppath, name), self)
2745 self.formatters[name] = formatter
2747 raise WebParameterError("Could not open dump '%s'" % name)
2750 def output_dumps(self, f):
2751 f.write(WEB_DUMPS_HEADER)
2752 f.write("<h3>List of available dumps</h3>")
2753 f.write("<table class=\"dumplist\">\n")
2754 f.write("<thead><tr>")
2755 f.write("<th>Name</th>")
2756 f.write("<th>File time</th>")
2757 f.write("<th>Comment</th>")
2758 f.write("</tr></thead>")
2760 for fname in os.listdir(self.dumppath):
2761 if DUMP_FILE_RE.match(fname):
2762 mtime = os.stat(os.path.join(self.dumppath, fname)).st_mtime
2763 fnames = dumps_by_time.get(mtime, [])
2764 fnames.append(fname)
2765 dumps_by_time[mtime] = fnames
2767 for mtime in sorted(dumps_by_time, reverse=True):
2768 fnames = dumps_by_time[mtime]
2769 for fname in fnames:
2771 f.write("<td><a href=\"summary.html?%s\">%s</a></td>\n" % (
2772 (urllib.urlencode({ 'dump' : fname }), fname)))
2773 f.write("<td> ")
2774 f.write(datetime.datetime.fromtimestamp(mtime))
2776 f.write("<td> ")
2777 self.output_dump_desc_field(f, fname)
2780 f.write("</table>\n")
2781 f.write(WEB_DUMPS_FOOTER)
2784 class InspectionShell(cmd.Cmd):
2785 def __init__(self, reader, heap):
2786 cmd.Cmd.__init__(self)
2787 self.reader = reader
2789 self.padawan = InspectionPadawan(reader, heap)
2790 self.prompt = "(grok) "
2792 def do_da(self, address):
2794 Print ASCII string starting at specified address.
2796 address = int(address, 16)
2798 while self.reader.IsValidAddress(address):
2799 code = self.reader.ReadU8(address)
2806 print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address)
2808 print "%s\n" % string
2810 def do_dd(self, address):
2812 Interpret memory at the given address (if available) as a sequence
2813 of words. Automatic alignment is not performed.
2815 start = int(address, 16)
2816 if (start & self.heap.ObjectAlignmentMask()) != 0:
2817 print "Warning: Dumping un-aligned memory, is this what you had in mind?"
2818 for slot in xrange(start,
2819 start + self.reader.PointerSize() * 10,
2820 self.reader.PointerSize()):
2821 if not self.reader.IsValidAddress(slot):
2822 print "Address is not contained within the minidump!"
2824 maybe_address = self.reader.ReadUIntPtr(slot)
2825 heap_object = self.padawan.SenseObject(maybe_address)
2826 print "%s: %s %s" % (self.reader.FormatIntPtr(slot),
2827 self.reader.FormatIntPtr(maybe_address),
2830 def do_do(self, address):
2832 Interpret memory at the given address as a V8 object. Automatic
2833 alignment makes sure that you can pass tagged as well as un-tagged
2836 address = int(address, 16)
2837 if (address & self.heap.ObjectAlignmentMask()) == 0:
2838 address = address + 1
2839 elif (address & self.heap.ObjectAlignmentMask()) != 1:
2840 print "Address doesn't look like a valid pointer!"
2842 heap_object = self.padawan.SenseObject(address)
2844 heap_object.Print(Printer())
2846 print "Address cannot be interpreted as object!"
2848 def do_do_desc(self, address):
2850 Print a descriptor array in a readable format.
2852 start = int(address, 16)
2853 if ((start & 1) == 1): start = start - 1
2854 DescriptorArray(FixedArray(self.heap, None, start)).Print(Printer())
2856 def do_do_map(self, address):
2858 Print a descriptor array in a readable format.
2860 start = int(address, 16)
2861 if ((start & 1) == 1): start = start - 1
2862 Map(self.heap, None, start).Print(Printer())
2864 def do_do_trans(self, address):
2866 Print a transition array in a readable format.
2868 start = int(address, 16)
2869 if ((start & 1) == 1): start = start - 1
2870 TransitionArray(FixedArray(self.heap, None, start)).Print(Printer())
2872 def do_dp(self, address):
2874 Interpret memory at the given address as being on a V8 heap page
2875 and print information about the page header (if available).
2877 address = int(address, 16)
2878 page_address = address & ~self.heap.PageAlignmentMask()
2879 if self.reader.IsValidAddress(page_address):
2880 raise NotImplementedError
2882 print "Page header is not available!"
2884 def do_k(self, arguments):
2886 Teach V8 heap layout information to the inspector. This increases
2887 the amount of annotations the inspector can produce while dumping
2888 data. The first page of each heap space is of particular interest
2889 because it contains known objects that do not move.
2891 self.padawan.PrintKnowledge()
2893 def do_kd(self, address):
2895 Teach V8 heap layout information to the inspector. Set the first
2896 data-space page by passing any pointer into that page.
2898 address = int(address, 16)
2899 page_address = address & ~self.heap.PageAlignmentMask()
2900 self.padawan.known_first_data_page = page_address
2902 def do_km(self, address):
2904 Teach V8 heap layout information to the inspector. Set the first
2905 map-space page by passing any pointer into that page.
2907 address = int(address, 16)
2908 page_address = address & ~self.heap.PageAlignmentMask()
2909 self.padawan.known_first_map_page = page_address
2911 def do_kp(self, address):
2913 Teach V8 heap layout information to the inspector. Set the first
2914 pointer-space page by passing any pointer into that page.
2916 address = int(address, 16)
2917 page_address = address & ~self.heap.PageAlignmentMask()
2918 self.padawan.known_first_pointer_page = page_address
2920 def do_list(self, smth):
2922 List all available memory regions.
2924 def print_region(reader, start, size, location):
2925 print " %s - %s (%d bytes)" % (reader.FormatIntPtr(start),
2926 reader.FormatIntPtr(start + size),
2928 print "Available memory regions:"
2929 self.reader.ForEachMemoryRegion(print_region)
2931 def do_lm(self, arg):
2933 List details for all loaded modules in the minidump. An argument can
2934 be passed to limit the output to only those modules that contain the
2935 argument as a substring (case insensitive match).
2937 for module in self.reader.module_list.modules:
2939 name = GetModuleName(self.reader, module).lower()
2940 if name.find(arg.lower()) >= 0:
2941 PrintModuleDetails(self.reader, module)
2943 PrintModuleDetails(self.reader, module)
2946 def do_s(self, word):
2948 Search for a given word in available memory regions. The given word
2949 is expanded to full pointer size and searched at aligned as well as
2950 un-aligned memory locations. Use 'sa' to search aligned locations
2956 print "Malformed word, prefix with '0x' to use hexadecimal format."
2958 print "Searching for word %d/0x%s:" % (word, self.reader.FormatIntPtr(word))
2959 self.reader.FindWord(word)
2961 def do_sh(self, none):
2963 Search for the V8 Heap object in all available memory regions. You
2964 might get lucky and find this rare treasure full of invaluable
2967 raise NotImplementedError
2969 def do_u(self, args):
2971 Unassemble memory in the region [address, address + size). If the
2972 size is not specified, a default value of 32 bytes is used.
2973 Synopsis: u 0x<address> 0x<size>
2975 args = args.split(' ')
2976 start = int(args[0], 16)
2977 size = int(args[1], 16) if len(args) > 1 else 0x20
2978 if not self.reader.IsValidAddress(start):
2979 print "Address is not contained within the minidump!"
2981 lines = self.reader.GetDisasmLines(start, size)
2983 print FormatDisasmLine(start, self.heap, line)
2986 def do_EOF(self, none):
2987 raise KeyboardInterrupt
2991 CONTEXT_FOR_ARCH = {
2992 MD_CPU_ARCHITECTURE_AMD64:
2993 ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip',
2994 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'],
2995 MD_CPU_ARCHITECTURE_ARM:
2996 ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
2997 'r10', 'r11', 'r12', 'sp', 'lr', 'pc'],
2998 MD_CPU_ARCHITECTURE_X86:
2999 ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
3002 KNOWN_MODULES = {'chrome.exe', 'chrome.dll'}
3004 def GetVersionString(ms, ls):
3005 return "%d.%d.%d.%d" % (ms >> 16, ms & 0xffff, ls >> 16, ls & 0xffff)
3008 def GetModuleName(reader, module):
3009 name = reader.ReadMinidumpString(module.module_name_rva)
3010 # simplify for path manipulation
3011 name = name.encode('utf-8')
3012 return str(os.path.basename(str(name).replace("\\", "/")))
3015 def PrintModuleDetails(reader, module):
3016 print "%s" % GetModuleName(reader, module)
3017 file_version = GetVersionString(module.version_info.dwFileVersionMS,
3018 module.version_info.dwFileVersionLS)
3019 product_version = GetVersionString(module.version_info.dwProductVersionMS,
3020 module.version_info.dwProductVersionLS)
3021 print " base: %s" % reader.FormatIntPtr(module.base_of_image)
3022 print " end: %s" % reader.FormatIntPtr(module.base_of_image +
3023 module.size_of_image)
3024 print " file version: %s" % file_version
3025 print " product version: %s" % product_version
3026 time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
3027 print " timestamp: %s" % time_date_stamp
3030 def AnalyzeMinidump(options, minidump_name):
3031 reader = MinidumpReader(options, minidump_name)
3033 DebugPrint("========================================")
3034 if reader.exception is None:
3035 print "Minidump has no exception info"
3037 print "Exception info:"
3038 exception_thread = reader.thread_map[reader.exception.thread_id]
3039 print " thread id: %d" % exception_thread.id
3040 print " code: %08X" % reader.exception.exception.code
3042 for r in CONTEXT_FOR_ARCH[reader.arch]:
3043 print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r)))
3044 # TODO(vitalyr): decode eflags.
3045 if reader.arch == MD_CPU_ARCHITECTURE_ARM:
3046 print " cpsr: %s" % bin(reader.exception_context.cpsr)[2:]
3048 print " eflags: %s" % bin(reader.exception_context.eflags)[2:]
3052 for module in reader.module_list.modules:
3053 name = GetModuleName(reader, module)
3054 if name in KNOWN_MODULES:
3055 print " %s at %08X" % (name, module.base_of_image)
3056 reader.TryLoadSymbolsFor(name, module)
3059 stack_top = reader.ExceptionSP()
3060 stack_bottom = exception_thread.stack.start + \
3061 exception_thread.stack.memory.data_size
3062 stack_map = {reader.ExceptionIP(): -1}
3063 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
3064 maybe_address = reader.ReadUIntPtr(slot)
3065 if not maybe_address in stack_map:
3066 stack_map[maybe_address] = slot
3067 heap = V8Heap(reader, stack_map)
3069 print "Disassembly around exception.eip:"
3070 eip_symbol = reader.FindSymbol(reader.ExceptionIP())
3071 if eip_symbol is not None:
3073 disasm_start = reader.ExceptionIP() - EIP_PROXIMITY
3074 disasm_bytes = 2 * EIP_PROXIMITY
3076 full_range = reader.FindRegion(reader.ExceptionIP())
3077 if full_range is not None:
3078 disasm_start = full_range[0]
3079 disasm_bytes = full_range[1]
3081 lines = reader.GetDisasmLines(disasm_start, disasm_bytes)
3084 print FormatDisasmLine(disasm_start, heap, line)
3088 heap = V8Heap(reader, None)
3091 FullDump(reader, heap)
3094 InspectionShell(reader, heap).onecmd(options.command)
3098 InspectionShell(reader, heap).cmdloop("type help to get help")
3099 except KeyboardInterrupt:
3101 elif not options.command:
3102 if reader.exception is not None:
3103 frame_pointer = reader.ExceptionFP()
3104 print "Annotated stack (from exception.esp to bottom):"
3105 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
3106 ascii_content = [c if c >= '\x20' and c < '\x7f' else '.'
3107 for c in reader.ReadBytes(slot, reader.PointerSize())]
3108 maybe_address = reader.ReadUIntPtr(slot)
3109 heap_object = heap.FindObject(maybe_address)
3110 maybe_symbol = reader.FindSymbol(maybe_address)
3111 if slot == frame_pointer:
3112 maybe_symbol = "<---- frame pointer"
3113 frame_pointer = maybe_address
3114 print "%s: %s %s %s" % (reader.FormatIntPtr(slot),
3115 reader.FormatIntPtr(maybe_address),
3116 "".join(ascii_content),
3119 heap_object.Print(Printer())
3125 if __name__ == "__main__":
3126 parser = optparse.OptionParser(USAGE)
3127 parser.add_option("-s", "--shell", dest="shell", action="store_true",
3128 help="start an interactive inspector shell")
3129 parser.add_option("-w", "--web", dest="web", action="store_true",
3130 help="start a web server on localhost:%i" % PORT_NUMBER)
3131 parser.add_option("-c", "--command", dest="command", default="",
3132 help="run an interactive inspector shell command and exit")
3133 parser.add_option("-f", "--full", dest="full", action="store_true",
3134 help="dump all information contained in the minidump")
3135 parser.add_option("--symdir", dest="symdir", default=".",
3136 help="directory containing *.pdb.sym file with symbols")
3137 parser.add_option("--objdump",
3138 default="/usr/bin/objdump",
3139 help="objdump tool to use [default: %default]")
3140 options, args = parser.parse_args()
3141 if os.path.exists(options.objdump):
3142 disasm.OBJDUMP_BIN = options.objdump
3143 OBJDUMP_BIN = options.objdump
3145 print "Cannot find %s, falling back to default objdump" % options.objdump
3151 server = InspectionWebServer(PORT_NUMBER, options, args[0])
3152 print 'Started httpserver on port ' , PORT_NUMBER
3153 webbrowser.open('http://localhost:%i/summary.html' % PORT_NUMBER)
3154 server.serve_forever()
3155 except KeyboardInterrupt:
3156 print '^C received, shutting down the web server'
3157 server.socket.close()
3159 AnalyzeMinidump(options, args[0])