blob: 468e7cc6b799ff5ffa69e125f01529155b19128e [file] [log] [blame]
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001#!/usr/bin/env python
2#
3# Copyright 2011 the V8 project authors. All rights reserved.
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met:
7#
8# * Redistributions of source code must retain the above copyright
9# notice, this list of conditions and the following disclaimer.
10# * Redistributions in binary form must reproduce the above
11# copyright notice, this list of conditions and the following
12# disclaimer in the documentation and/or other materials provided
13# with the distribution.
14# * Neither the name of Google Inc. nor the names of its
15# contributors may be used to endorse or promote products derived
16# from this software without specific prior written permission.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30import ctypes
31import mmap
32import optparse
33import os
34import disasm
35import sys
36import types
37import codecs
38import re
39
40
41USAGE="""usage: %prog [OPTION]...
42
43Minidump analyzer.
44
45Shows the processor state at the point of exception including the
46stack of the active thread and the referenced objects in the V8
47heap. Code objects are disassembled and the addresses linked from the
48stack (pushed return addresses) are marked with "=>".
49
50
51Examples:
52 $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp
53"""
54
55DEBUG=False
56
57
58def DebugPrint(s):
59 if not DEBUG: return
60 print s
61
62
63class Descriptor(object):
64 """Descriptor of a structure in a memory."""
65
66 def __init__(self, fields):
67 self.fields = fields
68 self.is_flexible = False
69 for _, type_or_func in fields:
70 if isinstance(type_or_func, types.FunctionType):
71 self.is_flexible = True
72 break
73 if not self.is_flexible:
74 self.ctype = Descriptor._GetCtype(fields)
75 self.size = ctypes.sizeof(self.ctype)
76
77 def Read(self, memory, offset):
78 if self.is_flexible:
79 fields_copy = self.fields[:]
80 last = 0
81 for name, type_or_func in fields_copy:
82 if isinstance(type_or_func, types.FunctionType):
83 partial_ctype = Descriptor._GetCtype(fields_copy[:last])
84 partial_object = partial_ctype.from_buffer(memory, offset)
85 type = type_or_func(partial_object)
86 if type is not None:
87 fields_copy[last] = (name, type)
88 last += 1
89 else:
90 last += 1
91 complete_ctype = Descriptor._GetCtype(fields_copy[:last])
92 else:
93 complete_ctype = self.ctype
94 return complete_ctype.from_buffer(memory, offset)
95
96 @staticmethod
97 def _GetCtype(fields):
98 class Raw(ctypes.Structure):
99 _fields_ = fields
100 _pack_ = 1
101
102 def __str__(self):
103 return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field))
104 for field, _ in Raw._fields_) + "}"
105 return Raw
106
107
108# Set of structures and constants that describe the layout of minidump
109# files. Based on MSDN and Google Breakpad.
110
111MINIDUMP_HEADER = Descriptor([
112 ("signature", ctypes.c_uint32),
113 ("version", ctypes.c_uint32),
114 ("stream_count", ctypes.c_uint32),
115 ("stream_directories_rva", ctypes.c_uint32),
116 ("checksum", ctypes.c_uint32),
117 ("time_date_stampt", ctypes.c_uint32),
118 ("flags", ctypes.c_uint64)
119])
120
121MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
122 ("data_size", ctypes.c_uint32),
123 ("rva", ctypes.c_uint32)
124])
125
126MINIDUMP_DIRECTORY = Descriptor([
127 ("stream_type", ctypes.c_uint32),
128 ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
129])
130
131MD_EXCEPTION_MAXIMUM_PARAMETERS = 15
132
133MINIDUMP_EXCEPTION = Descriptor([
134 ("code", ctypes.c_uint32),
135 ("flags", ctypes.c_uint32),
136 ("record", ctypes.c_uint64),
137 ("address", ctypes.c_uint64),
138 ("parameter_count", ctypes.c_uint32),
139 ("unused_alignment", ctypes.c_uint32),
140 ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS)
141])
142
143MINIDUMP_EXCEPTION_STREAM = Descriptor([
144 ("thread_id", ctypes.c_uint32),
145 ("unused_alignment", ctypes.c_uint32),
146 ("exception", MINIDUMP_EXCEPTION.ctype),
147 ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
148])
149
150# Stream types.
151MD_UNUSED_STREAM = 0
152MD_RESERVED_STREAM_0 = 1
153MD_RESERVED_STREAM_1 = 2
154MD_THREAD_LIST_STREAM = 3
155MD_MODULE_LIST_STREAM = 4
156MD_MEMORY_LIST_STREAM = 5
157MD_EXCEPTION_STREAM = 6
158MD_SYSTEM_INFO_STREAM = 7
159MD_THREAD_EX_LIST_STREAM = 8
160MD_MEMORY_64_LIST_STREAM = 9
161MD_COMMENT_STREAM_A = 10
162MD_COMMENT_STREAM_W = 11
163MD_HANDLE_DATA_STREAM = 12
164MD_FUNCTION_TABLE_STREAM = 13
165MD_UNLOADED_MODULE_LIST_STREAM = 14
166MD_MISC_INFO_STREAM = 15
167MD_MEMORY_INFO_LIST_STREAM = 16
168MD_THREAD_INFO_LIST_STREAM = 17
169MD_HANDLE_OPERATION_LIST_STREAM = 18
170
171MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80
172
173MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([
174 ("control_word", ctypes.c_uint32),
175 ("status_word", ctypes.c_uint32),
176 ("tag_word", ctypes.c_uint32),
177 ("error_offset", ctypes.c_uint32),
178 ("error_selector", ctypes.c_uint32),
179 ("data_offset", ctypes.c_uint32),
180 ("data_selector", ctypes.c_uint32),
181 ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE),
182 ("cr0_npx_state", ctypes.c_uint32)
183])
184
185MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512
186
187# Context flags.
188MD_CONTEXT_X86 = 0x00010000
189MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001)
190MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002)
191MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004)
192MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008)
193MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010)
194MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020)
195
196def EnableOnFlag(type, flag):
197 return lambda o: [None, type][int((o.context_flags & flag) != 0)]
198
199MINIDUMP_CONTEXT_X86 = Descriptor([
200 ("context_flags", ctypes.c_uint32),
201 # MD_CONTEXT_X86_DEBUG_REGISTERS.
202 ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
203 ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
204 ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
205 ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
206 ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
207 ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
208 # MD_CONTEXT_X86_FLOATING_POINT.
209 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype,
210 MD_CONTEXT_X86_FLOATING_POINT)),
211 # MD_CONTEXT_X86_SEGMENTS.
212 ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
213 ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
214 ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
215 ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
216 # MD_CONTEXT_X86_INTEGER.
217 ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
218 ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
219 ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
220 ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
221 ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
222 ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
223 # MD_CONTEXT_X86_CONTROL.
224 ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
225 ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
226 ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
227 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
228 ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
229 ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
230 # MD_CONTEXT_X86_EXTENDED_REGISTERS.
231 ("extended_registers",
232 EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE,
233 MD_CONTEXT_X86_EXTENDED_REGISTERS))
234])
235
236MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
237 ("start", ctypes.c_uint64),
238 ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
239])
240
241MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
242 ("start", ctypes.c_uint64),
243 ("size", ctypes.c_uint64)
244])
245
246MINIDUMP_MEMORY_LIST = Descriptor([
247 ("range_count", ctypes.c_uint32),
248 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
249])
250
251MINIDUMP_MEMORY_LIST64 = Descriptor([
252 ("range_count", ctypes.c_uint64),
253 ("base_rva", ctypes.c_uint64),
254 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count)
255])
256
257MINIDUMP_THREAD = Descriptor([
258 ("id", ctypes.c_uint32),
259 ("suspend_count", ctypes.c_uint32),
260 ("priority_class", ctypes.c_uint32),
261 ("priority", ctypes.c_uint32),
262 ("ted", ctypes.c_uint64),
263 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype),
264 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
265])
266
267MINIDUMP_THREAD_LIST = Descriptor([
268 ("thread_count", ctypes.c_uint32),
269 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
270])
271
272
273class MinidumpReader(object):
274 """Minidump (.dmp) reader."""
275
276 _HEADER_MAGIC = 0x504d444d
277
278 def __init__(self, options, minidump_name):
279 self.minidump_name = minidump_name
280 self.minidump_file = open(minidump_name, "r")
281 self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE)
282 self.header = MINIDUMP_HEADER.Read(self.minidump, 0)
283 if self.header.signature != MinidumpReader._HEADER_MAGIC:
284 print >>sys.stderr, "Warning: unsupported minidump header magic"
285 DebugPrint(self.header)
286 directories = []
287 offset = self.header.stream_directories_rva
288 for _ in xrange(self.header.stream_count):
289 directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
290 offset += MINIDUMP_DIRECTORY.size
291 self.exception = None
292 self.exception_context = None
293 self.memory_list = None
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000294 self.memory_list64 = None
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100295 self.thread_map = {}
296 for d in directories:
297 DebugPrint(d)
298 # TODO(vitalyr): extract system info including CPU features.
299 if d.stream_type == MD_EXCEPTION_STREAM:
300 self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
301 self.minidump, d.location.rva)
302 DebugPrint(self.exception)
303 self.exception_context = MINIDUMP_CONTEXT_X86.Read(
304 self.minidump, self.exception.thread_context.rva)
305 DebugPrint(self.exception_context)
306 elif d.stream_type == MD_THREAD_LIST_STREAM:
307 thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
308 assert ctypes.sizeof(thread_list) == d.location.data_size
309 DebugPrint(thread_list)
310 for thread in thread_list.threads:
311 DebugPrint(thread)
312 self.thread_map[thread.id] = thread
313 elif d.stream_type == MD_MEMORY_LIST_STREAM:
314 print >>sys.stderr, "Warning: not a full minidump"
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100315 assert self.memory_list is None
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000316 self.memory_list = MINIDUMP_MEMORY_LIST.Read(
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100317 self.minidump, d.location.rva)
318 assert ctypes.sizeof(self.memory_list) == d.location.data_size
319 DebugPrint(self.memory_list)
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000320 elif d.stream_type == MD_MEMORY_64_LIST_STREAM:
321 assert self.memory_list64 is None
322 self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read(
323 self.minidump, d.location.rva)
324 assert ctypes.sizeof(self.memory_list64) == d.location.data_size
325 DebugPrint(self.memory_list64)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100326
327 def IsValidAddress(self, address):
328 return self.FindLocation(address) is not None
329
330 def ReadU8(self, address):
331 location = self.FindLocation(address)
332 return ctypes.c_uint8.from_buffer(self.minidump, location).value
333
334 def ReadU32(self, address):
335 location = self.FindLocation(address)
336 return ctypes.c_uint32.from_buffer(self.minidump, location).value
337
338 def ReadBytes(self, address, size):
339 location = self.FindLocation(address)
340 return self.minidump[location:location + size]
341
342 def FindLocation(self, address):
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100343 offset = 0
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000344 if self.memory_list64 is not None:
345 for r in self.memory_list64.ranges:
346 if r.start <= address < r.start + r.size:
347 return self.memory_list64.base_rva + offset + address - r.start
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100348 offset += r.size
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000349 if self.memory_list is not None:
350 for r in self.memory_list.ranges:
351 if r.start <= address < r.start + r.memory.data_size:
352 return r.memory.rva + address - r.start
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100353 return None
354
355 def GetDisasmLines(self, address, size):
356 location = self.FindLocation(address)
357 if location is None: return []
358 return disasm.GetDisasmLines(self.minidump_name,
359 location,
360 size,
361 "ia32",
362 False)
363
364
365 def Dispose(self):
366 self.minidump.close()
367 self.minidump_file.close()
368
369
370# List of V8 instance types. Obtained by adding the code below to any .cc file.
371#
372# #define DUMP_TYPE(T) printf("%d: \"%s\",\n", T, #T);
373# struct P {
374# P() {
375# printf("{\n");
376# INSTANCE_TYPE_LIST(DUMP_TYPE)
377# printf("}\n");
378# }
379# };
380# static P p;
381INSTANCE_TYPES = {
Ben Murdoch3fb3ca82011-12-02 17:19:32 +000038264: "SYMBOL_TYPE",
38368: "ASCII_SYMBOL_TYPE",
38465: "CONS_SYMBOL_TYPE",
38569: "CONS_ASCII_SYMBOL_TYPE",
38666: "EXTERNAL_SYMBOL_TYPE",
38774: "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE",
38870: "EXTERNAL_ASCII_SYMBOL_TYPE",
3890: "STRING_TYPE",
3904: "ASCII_STRING_TYPE",
3911: "CONS_STRING_TYPE",
3925: "CONS_ASCII_STRING_TYPE",
3932: "EXTERNAL_STRING_TYPE",
39410: "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE",
3956: "EXTERNAL_ASCII_STRING_TYPE",
3966: "PRIVATE_EXTERNAL_ASCII_STRING_TYPE",
397128: "MAP_TYPE",
398129: "CODE_TYPE",
399130: "ODDBALL_TYPE",
400131: "JS_GLOBAL_PROPERTY_CELL_TYPE",
401132: "HEAP_NUMBER_TYPE",
402133: "FOREIGN_TYPE",
403134: "BYTE_ARRAY_TYPE",
404135: "EXTERNAL_BYTE_ARRAY_TYPE",
405136: "EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE",
406137: "EXTERNAL_SHORT_ARRAY_TYPE",
407138: "EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE",
408139: "EXTERNAL_INT_ARRAY_TYPE",
409140: "EXTERNAL_UNSIGNED_INT_ARRAY_TYPE",
410141: "EXTERNAL_FLOAT_ARRAY_TYPE",
411143: "EXTERNAL_PIXEL_ARRAY_TYPE",
412145: "FILLER_TYPE",
413146: "ACCESSOR_INFO_TYPE",
414147: "ACCESS_CHECK_INFO_TYPE",
415148: "INTERCEPTOR_INFO_TYPE",
416149: "CALL_HANDLER_INFO_TYPE",
417150: "FUNCTION_TEMPLATE_INFO_TYPE",
418151: "OBJECT_TEMPLATE_INFO_TYPE",
419152: "SIGNATURE_INFO_TYPE",
420153: "TYPE_SWITCH_INFO_TYPE",
421154: "SCRIPT_TYPE",
422155: "CODE_CACHE_TYPE",
423156: "POLYMORPHIC_CODE_CACHE_TYPE",
424159: "FIXED_ARRAY_TYPE",
425160: "SHARED_FUNCTION_INFO_TYPE",
426161: "JS_MESSAGE_OBJECT_TYPE",
427162: "JS_VALUE_TYPE",
428163: "JS_OBJECT_TYPE",
429164: "JS_CONTEXT_EXTENSION_OBJECT_TYPE",
430165: "JS_GLOBAL_OBJECT_TYPE",
431166: "JS_BUILTINS_OBJECT_TYPE",
432167: "JS_GLOBAL_PROXY_TYPE",
433168: "JS_ARRAY_TYPE",
434169: "JS_PROXY_TYPE",
435170: "JS_REGEXP_TYPE",
436171: "JS_FUNCTION_TYPE",
437172: "JS_FUNCTION_PROXY_TYPE",
438157: "DEBUG_INFO_TYPE",
439158: "BREAK_POINT_INFO_TYPE",
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100440}
441
442
443class Printer(object):
444 """Printer with indentation support."""
445
446 def __init__(self):
447 self.indent = 0
448
449 def Indent(self):
450 self.indent += 2
451
452 def Dedent(self):
453 self.indent -= 2
454
455 def Print(self, string):
456 print "%s%s" % (self._IndentString(), string)
457
458 def PrintLines(self, lines):
459 indent = self._IndentString()
460 print "\n".join("%s%s" % (indent, line) for line in lines)
461
462 def _IndentString(self):
463 return self.indent * " "
464
465
466ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
467
468
469def FormatDisasmLine(start, heap, line):
470 line_address = start + line[0]
471 stack_slot = heap.stack_map.get(line_address)
472 marker = " "
473 if stack_slot:
474 marker = "=>"
475 code = AnnotateAddresses(heap, line[1])
476 return "%s%08x %08x: %s" % (marker, line_address, line[0], code)
477
478
479def AnnotateAddresses(heap, line):
480 extra = []
481 for m in ADDRESS_RE.finditer(line):
482 maybe_address = int(m.group(0), 16)
483 object = heap.FindObject(maybe_address)
484 if not object: continue
485 extra.append(str(object))
486 if len(extra) == 0: return line
487 return "%s ;; %s" % (line, ", ".join(extra))
488
489
490class HeapObject(object):
491 def __init__(self, heap, map, address):
492 self.heap = heap
493 self.map = map
494 self.address = address
495
496 def Is(self, cls):
497 return isinstance(self, cls)
498
499 def Print(self, p):
500 p.Print(str(self))
501
502 def __str__(self):
503 return "HeapObject(%08x, %s)" % (self.address,
504 INSTANCE_TYPES[self.map.instance_type])
505
506 def ObjectField(self, offset):
507 field_value = self.heap.reader.ReadU32(self.address + offset)
508 return self.heap.FindObjectOrSmi(field_value)
509
510 def SmiField(self, offset):
511 field_value = self.heap.reader.ReadU32(self.address + offset)
512 assert (field_value & 1) == 0
513 return field_value / 2
514
515
516class Map(HeapObject):
517 INSTANCE_TYPE_OFFSET = 8
518
519 def __init__(self, heap, map, address):
520 HeapObject.__init__(self, heap, map, address)
521 self.instance_type = \
522 heap.reader.ReadU8(self.address + Map.INSTANCE_TYPE_OFFSET)
523
524
525class String(HeapObject):
526 LENGTH_OFFSET = 4
527
528 def __init__(self, heap, map, address):
529 HeapObject.__init__(self, heap, map, address)
530 self.length = self.SmiField(String.LENGTH_OFFSET)
531
532 def GetChars(self):
533 return "?string?"
534
535 def Print(self, p):
536 p.Print(str(self))
537
538 def __str__(self):
539 return "\"%s\"" % self.GetChars()
540
541
542class SeqString(String):
543 CHARS_OFFSET = 12
544
545 def __init__(self, heap, map, address):
546 String.__init__(self, heap, map, address)
547 self.chars = heap.reader.ReadBytes(self.address + SeqString.CHARS_OFFSET,
548 self.length)
549
550 def GetChars(self):
551 return self.chars
552
553
554class ExternalString(String):
555 RESOURCE_OFFSET = 12
556
557 WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
558 WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
559
560 def __init__(self, heap, map, address):
561 String.__init__(self, heap, map, address)
562 reader = heap.reader
563 self.resource = \
564 reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
565 self.chars = "?external string?"
566 if not reader.IsValidAddress(self.resource): return
567 string_impl_address = self.resource + \
568 ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
569 if not reader.IsValidAddress(string_impl_address): return
570 string_impl = reader.ReadU32(string_impl_address)
571 chars_ptr_address = string_impl + \
572 ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
573 if not reader.IsValidAddress(chars_ptr_address): return
574 chars_ptr = reader.ReadU32(chars_ptr_address)
575 if not reader.IsValidAddress(chars_ptr): return
576 raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
577 self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
578
579 def GetChars(self):
580 return self.chars
581
582
583class ConsString(String):
584 LEFT_OFFSET = 12
585 RIGHT_OFFSET = 16
586
587 def __init__(self, heap, map, address):
588 String.__init__(self, heap, map, address)
589 self.left = self.ObjectField(ConsString.LEFT_OFFSET)
590 self.right = self.ObjectField(ConsString.RIGHT_OFFSET)
591
592 def GetChars(self):
593 return self.left.GetChars() + self.right.GetChars()
594
595
596class Oddball(HeapObject):
597 TO_STRING_OFFSET = 4
598
599 def __init__(self, heap, map, address):
600 HeapObject.__init__(self, heap, map, address)
601 self.to_string = self.ObjectField(Oddball.TO_STRING_OFFSET)
602
603 def Print(self, p):
604 p.Print(str(self))
605
606 def __str__(self):
607 return "<%s>" % self.to_string.GetChars()
608
609
610class FixedArray(HeapObject):
611 LENGTH_OFFSET = 4
612 ELEMENTS_OFFSET = 8
613
614 def __init__(self, heap, map, address):
615 HeapObject.__init__(self, heap, map, address)
616 self.length = self.SmiField(FixedArray.LENGTH_OFFSET)
617
618 def Print(self, p):
619 p.Print("FixedArray(%08x) {" % self.address)
620 p.Indent()
621 p.Print("length: %d" % self.length)
622 for i in xrange(self.length):
623 offset = FixedArray.ELEMENTS_OFFSET + 4 * i
624 p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
625 p.Dedent()
626 p.Print("}")
627
628 def __str__(self):
629 return "FixedArray(%08x, length=%d)" % (self.address, self.length)
630
631
632class JSFunction(HeapObject):
633 CODE_ENTRY_OFFSET = 12
634 SHARED_OFFSET = 20
635
636 def __init__(self, heap, map, address):
637 HeapObject.__init__(self, heap, map, address)
638 code_entry = \
639 heap.reader.ReadU32(self.address + JSFunction.CODE_ENTRY_OFFSET)
640 self.code = heap.FindObject(code_entry - Code.ENTRY_OFFSET + 1)
641 self.shared = self.ObjectField(JSFunction.SHARED_OFFSET)
642
643 def Print(self, p):
644 source = "\n".join(" %s" % line for line in self._GetSource().split("\n"))
645 p.Print("JSFunction(%08x) {" % self.address)
646 p.Indent()
647 p.Print("inferred name: %s" % self.shared.inferred_name)
648 if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
649 p.Print("script name: %s" % self.shared.script.name)
650 p.Print("source:")
651 p.PrintLines(self._GetSource().split("\n"))
652 p.Print("code:")
653 self.code.Print(p)
654 if self.code != self.shared.code:
655 p.Print("unoptimized code:")
656 self.shared.code.Print(p)
657 p.Dedent()
658 p.Print("}")
659
660 def __str__(self):
661 inferred_name = ""
662 if self.shared.Is(SharedFunctionInfo):
663 inferred_name = self.shared.inferred_name
664 return "JSFunction(%08x, %s)" % (self.address, inferred_name)
665
666 def _GetSource(self):
667 source = "?source?"
668 start = self.shared.start_position
669 end = self.shared.end_position
670 if not self.shared.script.Is(Script): return source
671 script_source = self.shared.script.source
672 if not script_source.Is(String): return source
673 return script_source.GetChars()[start:end]
674
675
676class SharedFunctionInfo(HeapObject):
677 CODE_OFFSET = 2 * 4
678 SCRIPT_OFFSET = 7 * 4
679 INFERRED_NAME_OFFSET = 9 * 4
680 START_POSITION_AND_TYPE_OFFSET = 17 * 4
681 END_POSITION_OFFSET = 18 * 4
682
683 def __init__(self, heap, map, address):
684 HeapObject.__init__(self, heap, map, address)
685 self.code = self.ObjectField(SharedFunctionInfo.CODE_OFFSET)
686 self.script = self.ObjectField(SharedFunctionInfo.SCRIPT_OFFSET)
687 self.inferred_name = \
688 self.ObjectField(SharedFunctionInfo.INFERRED_NAME_OFFSET)
689 start_position_and_type = \
690 self.SmiField(SharedFunctionInfo.START_POSITION_AND_TYPE_OFFSET)
691 self.start_position = start_position_and_type >> 2
692 self.end_position = self.SmiField(SharedFunctionInfo.END_POSITION_OFFSET)
693
694
695class Script(HeapObject):
696 SOURCE_OFFSET = 4
697 NAME_OFFSET = 8
698
699 def __init__(self, heap, map, address):
700 HeapObject.__init__(self, heap, map, address)
701 self.source = self.ObjectField(Script.SOURCE_OFFSET)
702 self.name = self.ObjectField(Script.NAME_OFFSET)
703
704
705class Code(HeapObject):
706 INSTRUCTION_SIZE_OFFSET = 4
707 ENTRY_OFFSET = 32
708
709 def __init__(self, heap, map, address):
710 HeapObject.__init__(self, heap, map, address)
711 self.entry = self.address + Code.ENTRY_OFFSET
712 self.instruction_size = \
713 heap.reader.ReadU32(self.address + Code.INSTRUCTION_SIZE_OFFSET)
714
715 def Print(self, p):
716 lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
717 p.Print("Code(%08x) {" % self.address)
718 p.Indent()
719 p.Print("instruction_size: %d" % self.instruction_size)
720 p.PrintLines(self._FormatLine(line) for line in lines)
721 p.Dedent()
722 p.Print("}")
723
724 def _FormatLine(self, line):
725 return FormatDisasmLine(self.entry, self.heap, line)
726
727
728class V8Heap(object):
729 CLASS_MAP = {
730 "SYMBOL_TYPE": SeqString,
731 "ASCII_SYMBOL_TYPE": SeqString,
732 "CONS_SYMBOL_TYPE": ConsString,
733 "CONS_ASCII_SYMBOL_TYPE": ConsString,
734 "EXTERNAL_SYMBOL_TYPE": ExternalString,
735 "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
736 "EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
737 "STRING_TYPE": SeqString,
738 "ASCII_STRING_TYPE": SeqString,
739 "CONS_STRING_TYPE": ConsString,
740 "CONS_ASCII_STRING_TYPE": ConsString,
741 "EXTERNAL_STRING_TYPE": ExternalString,
742 "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE": ExternalString,
743 "EXTERNAL_ASCII_STRING_TYPE": ExternalString,
744
745 "MAP_TYPE": Map,
746 "ODDBALL_TYPE": Oddball,
747 "FIXED_ARRAY_TYPE": FixedArray,
748 "JS_FUNCTION_TYPE": JSFunction,
749 "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
750 "SCRIPT_TYPE": Script,
751 "CODE_TYPE": Code
752 }
753
754 def __init__(self, reader, stack_map):
755 self.reader = reader
756 self.stack_map = stack_map
757 self.objects = {}
758
759 def FindObjectOrSmi(self, tagged_address):
760 if (tagged_address & 1) == 0: return tagged_address / 2
761 return self.FindObject(tagged_address)
762
763 def FindObject(self, tagged_address):
764 if tagged_address in self.objects:
765 return self.objects[tagged_address]
766 if (tagged_address & 1) != 1: return None
767 address = tagged_address - 1
768 if not self.reader.IsValidAddress(address): return None
769 map_tagged_address = self.reader.ReadU32(address)
770 if tagged_address == map_tagged_address:
771 # Meta map?
772 meta_map = Map(self, None, address)
773 instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
774 if instance_type_name != "MAP_TYPE": return None
775 meta_map.map = meta_map
776 object = meta_map
777 else:
778 map = self.FindObject(map_tagged_address)
779 if map is None: return None
780 instance_type_name = INSTANCE_TYPES.get(map.instance_type)
781 if instance_type_name is None: return None
782 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
783 object = cls(self, map, address)
784 self.objects[tagged_address] = object
785 return object
786
787
788EIP_PROXIMITY = 64
789
790
791def AnalyzeMinidump(options, minidump_name):
792 reader = MinidumpReader(options, minidump_name)
793 DebugPrint("========================================")
794 if reader.exception is None:
795 print "Minidump has no exception info"
796 return
797 print "Exception info:"
798 exception_thread = reader.thread_map[reader.exception.thread_id]
799 print " thread id: %d" % exception_thread.id
800 print " code: %08X" % reader.exception.exception.code
801 print " context:"
802 print " eax: %08x" % reader.exception_context.eax
803 print " ebx: %08x" % reader.exception_context.ebx
804 print " ecx: %08x" % reader.exception_context.ecx
805 print " edx: %08x" % reader.exception_context.edx
806 print " edi: %08x" % reader.exception_context.edi
807 print " esi: %08x" % reader.exception_context.esi
808 print " ebp: %08x" % reader.exception_context.ebp
809 print " esp: %08x" % reader.exception_context.esp
810 print " eip: %08x" % reader.exception_context.eip
811 # TODO(vitalyr): decode eflags.
812 print " eflags: %s" % bin(reader.exception_context.eflags)[2:]
813 print
814
815 stack_bottom = exception_thread.stack.start + \
816 exception_thread.stack.memory.data_size
817 stack_map = {reader.exception_context.eip: -1}
818 for slot in xrange(reader.exception_context.esp, stack_bottom, 4):
819 maybe_address = reader.ReadU32(slot)
820 if not maybe_address in stack_map:
821 stack_map[maybe_address] = slot
822 heap = V8Heap(reader, stack_map)
823
824 print "Disassembly around exception.eip:"
825 start = reader.exception_context.eip - EIP_PROXIMITY
826 lines = reader.GetDisasmLines(start, 2 * EIP_PROXIMITY)
827 for line in lines:
828 print FormatDisasmLine(start, heap, line)
829 print
830
831 print "Annotated stack (from exception.esp to bottom):"
832 for slot in xrange(reader.exception_context.esp, stack_bottom, 4):
833 maybe_address = reader.ReadU32(slot)
834 heap_object = heap.FindObject(maybe_address)
835 print "%08x: %08x" % (slot, maybe_address)
836 if heap_object:
837 heap_object.Print(Printer())
838 print
839
840 reader.Dispose()
841
842
843if __name__ == "__main__":
844 parser = optparse.OptionParser(USAGE)
845 options, args = parser.parse_args()
846 if len(args) != 1:
847 parser.print_help()
848 sys.exit(1)
849 AnalyzeMinidump(options, args[0])