blob: 15d09f4cd3ce8f8336b05da0b8977d4b8005c338 [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
294 self.thread_map = {}
295 for d in directories:
296 DebugPrint(d)
297 # TODO(vitalyr): extract system info including CPU features.
298 if d.stream_type == MD_EXCEPTION_STREAM:
299 self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
300 self.minidump, d.location.rva)
301 DebugPrint(self.exception)
302 self.exception_context = MINIDUMP_CONTEXT_X86.Read(
303 self.minidump, self.exception.thread_context.rva)
304 DebugPrint(self.exception_context)
305 elif d.stream_type == MD_THREAD_LIST_STREAM:
306 thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
307 assert ctypes.sizeof(thread_list) == d.location.data_size
308 DebugPrint(thread_list)
309 for thread in thread_list.threads:
310 DebugPrint(thread)
311 self.thread_map[thread.id] = thread
312 elif d.stream_type == MD_MEMORY_LIST_STREAM:
313 print >>sys.stderr, "Warning: not a full minidump"
314 ml = MINIDUMP_MEMORY_LIST.Read(self.minidump, d.location.rva)
315 DebugPrint(ml)
316 for m in ml.ranges:
317 DebugPrint(m)
318 elif d.stream_type == MD_MEMORY_64_LIST_STREAM:
319 assert self.memory_list is None
320 self.memory_list = MINIDUMP_MEMORY_LIST64.Read(
321 self.minidump, d.location.rva)
322 assert ctypes.sizeof(self.memory_list) == d.location.data_size
323 DebugPrint(self.memory_list)
324
325 def IsValidAddress(self, address):
326 return self.FindLocation(address) is not None
327
328 def ReadU8(self, address):
329 location = self.FindLocation(address)
330 return ctypes.c_uint8.from_buffer(self.minidump, location).value
331
332 def ReadU32(self, address):
333 location = self.FindLocation(address)
334 return ctypes.c_uint32.from_buffer(self.minidump, location).value
335
336 def ReadBytes(self, address, size):
337 location = self.FindLocation(address)
338 return self.minidump[location:location + size]
339
340 def FindLocation(self, address):
341 # TODO(vitalyr): only works for full minidumps (...64 structure variants).
342 offset = 0
343 for r in self.memory_list.ranges:
344 if r.start <= address < r.start + r.size:
345 return self.memory_list.base_rva + offset + address - r.start
346 offset += r.size
347 return None
348
349 def GetDisasmLines(self, address, size):
350 location = self.FindLocation(address)
351 if location is None: return []
352 return disasm.GetDisasmLines(self.minidump_name,
353 location,
354 size,
355 "ia32",
356 False)
357
358
359 def Dispose(self):
360 self.minidump.close()
361 self.minidump_file.close()
362
363
364# List of V8 instance types. Obtained by adding the code below to any .cc file.
365#
366# #define DUMP_TYPE(T) printf("%d: \"%s\",\n", T, #T);
367# struct P {
368# P() {
369# printf("{\n");
370# INSTANCE_TYPE_LIST(DUMP_TYPE)
371# printf("}\n");
372# }
373# };
374# static P p;
375INSTANCE_TYPES = {
376 64: "SYMBOL_TYPE",
377 68: "ASCII_SYMBOL_TYPE",
378 65: "CONS_SYMBOL_TYPE",
379 69: "CONS_ASCII_SYMBOL_TYPE",
380 66: "EXTERNAL_SYMBOL_TYPE",
381 74: "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE",
382 70: "EXTERNAL_ASCII_SYMBOL_TYPE",
383 0: "STRING_TYPE",
384 4: "ASCII_STRING_TYPE",
385 1: "CONS_STRING_TYPE",
386 5: "CONS_ASCII_STRING_TYPE",
387 2: "EXTERNAL_STRING_TYPE",
388 10: "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE",
389 6: "EXTERNAL_ASCII_STRING_TYPE",
390 6: "PRIVATE_EXTERNAL_ASCII_STRING_TYPE",
391 128: "MAP_TYPE",
392 129: "CODE_TYPE",
393 130: "ODDBALL_TYPE",
394 131: "JS_GLOBAL_PROPERTY_CELL_TYPE",
395 132: "HEAP_NUMBER_TYPE",
Ben Murdoch257744e2011-11-30 15:57:28 +0000396 134: "JS_PROXY_TYPE",
397 133: "FOREIGN_TYPE",
398 135: "BYTE_ARRAY_TYPE",
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100399 136: "EXTERNAL_BYTE_ARRAY_TYPE",
400 137: "EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE",
401 138: "EXTERNAL_SHORT_ARRAY_TYPE",
402 139: "EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE",
403 140: "EXTERNAL_INT_ARRAY_TYPE",
404 141: "EXTERNAL_UNSIGNED_INT_ARRAY_TYPE",
405 142: "EXTERNAL_FLOAT_ARRAY_TYPE",
Ben Murdoch257744e2011-11-30 15:57:28 +0000406 144: "EXTERNAL_PIXEL_ARRAY_TYPE",
407 145: "FILLER_TYPE",
408 146: "ACCESSOR_INFO_TYPE",
409 147: "ACCESS_CHECK_INFO_TYPE",
410 148: "INTERCEPTOR_INFO_TYPE",
411 149: "CALL_HANDLER_INFO_TYPE",
412 150: "FUNCTION_TEMPLATE_INFO_TYPE",
413 151: "OBJECT_TEMPLATE_INFO_TYPE",
414 152: "SIGNATURE_INFO_TYPE",
415 153: "TYPE_SWITCH_INFO_TYPE",
416 154: "SCRIPT_TYPE",
417 155: "CODE_CACHE_TYPE",
418 158: "FIXED_ARRAY_TYPE",
419 159: "SHARED_FUNCTION_INFO_TYPE",
420 160: "JS_MESSAGE_OBJECT_TYPE",
421 161: "JS_VALUE_TYPE",
422 162: "JS_OBJECT_TYPE",
423 163: "JS_CONTEXT_EXTENSION_OBJECT_TYPE",
424 164: "JS_GLOBAL_OBJECT_TYPE",
425 165: "JS_BUILTINS_OBJECT_TYPE",
426 166: "JS_GLOBAL_PROXY_TYPE",
427 167: "JS_ARRAY_TYPE",
428 168: "JS_REGEXP_TYPE",
429 169: "JS_FUNCTION_TYPE",
430 156: "DEBUG_INFO_TYPE",
431 157: "BREAK_POINT_INFO_TYPE",
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100432}
433
434
435class Printer(object):
436 """Printer with indentation support."""
437
438 def __init__(self):
439 self.indent = 0
440
441 def Indent(self):
442 self.indent += 2
443
444 def Dedent(self):
445 self.indent -= 2
446
447 def Print(self, string):
448 print "%s%s" % (self._IndentString(), string)
449
450 def PrintLines(self, lines):
451 indent = self._IndentString()
452 print "\n".join("%s%s" % (indent, line) for line in lines)
453
454 def _IndentString(self):
455 return self.indent * " "
456
457
458ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
459
460
461def FormatDisasmLine(start, heap, line):
462 line_address = start + line[0]
463 stack_slot = heap.stack_map.get(line_address)
464 marker = " "
465 if stack_slot:
466 marker = "=>"
467 code = AnnotateAddresses(heap, line[1])
468 return "%s%08x %08x: %s" % (marker, line_address, line[0], code)
469
470
471def AnnotateAddresses(heap, line):
472 extra = []
473 for m in ADDRESS_RE.finditer(line):
474 maybe_address = int(m.group(0), 16)
475 object = heap.FindObject(maybe_address)
476 if not object: continue
477 extra.append(str(object))
478 if len(extra) == 0: return line
479 return "%s ;; %s" % (line, ", ".join(extra))
480
481
482class HeapObject(object):
483 def __init__(self, heap, map, address):
484 self.heap = heap
485 self.map = map
486 self.address = address
487
488 def Is(self, cls):
489 return isinstance(self, cls)
490
491 def Print(self, p):
492 p.Print(str(self))
493
494 def __str__(self):
495 return "HeapObject(%08x, %s)" % (self.address,
496 INSTANCE_TYPES[self.map.instance_type])
497
498 def ObjectField(self, offset):
499 field_value = self.heap.reader.ReadU32(self.address + offset)
500 return self.heap.FindObjectOrSmi(field_value)
501
502 def SmiField(self, offset):
503 field_value = self.heap.reader.ReadU32(self.address + offset)
504 assert (field_value & 1) == 0
505 return field_value / 2
506
507
508class Map(HeapObject):
509 INSTANCE_TYPE_OFFSET = 8
510
511 def __init__(self, heap, map, address):
512 HeapObject.__init__(self, heap, map, address)
513 self.instance_type = \
514 heap.reader.ReadU8(self.address + Map.INSTANCE_TYPE_OFFSET)
515
516
517class String(HeapObject):
518 LENGTH_OFFSET = 4
519
520 def __init__(self, heap, map, address):
521 HeapObject.__init__(self, heap, map, address)
522 self.length = self.SmiField(String.LENGTH_OFFSET)
523
524 def GetChars(self):
525 return "?string?"
526
527 def Print(self, p):
528 p.Print(str(self))
529
530 def __str__(self):
531 return "\"%s\"" % self.GetChars()
532
533
534class SeqString(String):
535 CHARS_OFFSET = 12
536
537 def __init__(self, heap, map, address):
538 String.__init__(self, heap, map, address)
539 self.chars = heap.reader.ReadBytes(self.address + SeqString.CHARS_OFFSET,
540 self.length)
541
542 def GetChars(self):
543 return self.chars
544
545
546class ExternalString(String):
547 RESOURCE_OFFSET = 12
548
549 WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
550 WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
551
552 def __init__(self, heap, map, address):
553 String.__init__(self, heap, map, address)
554 reader = heap.reader
555 self.resource = \
556 reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
557 self.chars = "?external string?"
558 if not reader.IsValidAddress(self.resource): return
559 string_impl_address = self.resource + \
560 ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
561 if not reader.IsValidAddress(string_impl_address): return
562 string_impl = reader.ReadU32(string_impl_address)
563 chars_ptr_address = string_impl + \
564 ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
565 if not reader.IsValidAddress(chars_ptr_address): return
566 chars_ptr = reader.ReadU32(chars_ptr_address)
567 if not reader.IsValidAddress(chars_ptr): return
568 raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
569 self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
570
571 def GetChars(self):
572 return self.chars
573
574
575class ConsString(String):
576 LEFT_OFFSET = 12
577 RIGHT_OFFSET = 16
578
579 def __init__(self, heap, map, address):
580 String.__init__(self, heap, map, address)
581 self.left = self.ObjectField(ConsString.LEFT_OFFSET)
582 self.right = self.ObjectField(ConsString.RIGHT_OFFSET)
583
584 def GetChars(self):
585 return self.left.GetChars() + self.right.GetChars()
586
587
588class Oddball(HeapObject):
589 TO_STRING_OFFSET = 4
590
591 def __init__(self, heap, map, address):
592 HeapObject.__init__(self, heap, map, address)
593 self.to_string = self.ObjectField(Oddball.TO_STRING_OFFSET)
594
595 def Print(self, p):
596 p.Print(str(self))
597
598 def __str__(self):
599 return "<%s>" % self.to_string.GetChars()
600
601
602class FixedArray(HeapObject):
603 LENGTH_OFFSET = 4
604 ELEMENTS_OFFSET = 8
605
606 def __init__(self, heap, map, address):
607 HeapObject.__init__(self, heap, map, address)
608 self.length = self.SmiField(FixedArray.LENGTH_OFFSET)
609
610 def Print(self, p):
611 p.Print("FixedArray(%08x) {" % self.address)
612 p.Indent()
613 p.Print("length: %d" % self.length)
614 for i in xrange(self.length):
615 offset = FixedArray.ELEMENTS_OFFSET + 4 * i
616 p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
617 p.Dedent()
618 p.Print("}")
619
620 def __str__(self):
621 return "FixedArray(%08x, length=%d)" % (self.address, self.length)
622
623
624class JSFunction(HeapObject):
625 CODE_ENTRY_OFFSET = 12
626 SHARED_OFFSET = 20
627
628 def __init__(self, heap, map, address):
629 HeapObject.__init__(self, heap, map, address)
630 code_entry = \
631 heap.reader.ReadU32(self.address + JSFunction.CODE_ENTRY_OFFSET)
632 self.code = heap.FindObject(code_entry - Code.ENTRY_OFFSET + 1)
633 self.shared = self.ObjectField(JSFunction.SHARED_OFFSET)
634
635 def Print(self, p):
636 source = "\n".join(" %s" % line for line in self._GetSource().split("\n"))
637 p.Print("JSFunction(%08x) {" % self.address)
638 p.Indent()
639 p.Print("inferred name: %s" % self.shared.inferred_name)
640 if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
641 p.Print("script name: %s" % self.shared.script.name)
642 p.Print("source:")
643 p.PrintLines(self._GetSource().split("\n"))
644 p.Print("code:")
645 self.code.Print(p)
646 if self.code != self.shared.code:
647 p.Print("unoptimized code:")
648 self.shared.code.Print(p)
649 p.Dedent()
650 p.Print("}")
651
652 def __str__(self):
653 inferred_name = ""
654 if self.shared.Is(SharedFunctionInfo):
655 inferred_name = self.shared.inferred_name
656 return "JSFunction(%08x, %s)" % (self.address, inferred_name)
657
658 def _GetSource(self):
659 source = "?source?"
660 start = self.shared.start_position
661 end = self.shared.end_position
662 if not self.shared.script.Is(Script): return source
663 script_source = self.shared.script.source
664 if not script_source.Is(String): return source
665 return script_source.GetChars()[start:end]
666
667
668class SharedFunctionInfo(HeapObject):
669 CODE_OFFSET = 2 * 4
670 SCRIPT_OFFSET = 7 * 4
671 INFERRED_NAME_OFFSET = 9 * 4
672 START_POSITION_AND_TYPE_OFFSET = 17 * 4
673 END_POSITION_OFFSET = 18 * 4
674
675 def __init__(self, heap, map, address):
676 HeapObject.__init__(self, heap, map, address)
677 self.code = self.ObjectField(SharedFunctionInfo.CODE_OFFSET)
678 self.script = self.ObjectField(SharedFunctionInfo.SCRIPT_OFFSET)
679 self.inferred_name = \
680 self.ObjectField(SharedFunctionInfo.INFERRED_NAME_OFFSET)
681 start_position_and_type = \
682 self.SmiField(SharedFunctionInfo.START_POSITION_AND_TYPE_OFFSET)
683 self.start_position = start_position_and_type >> 2
684 self.end_position = self.SmiField(SharedFunctionInfo.END_POSITION_OFFSET)
685
686
687class Script(HeapObject):
688 SOURCE_OFFSET = 4
689 NAME_OFFSET = 8
690
691 def __init__(self, heap, map, address):
692 HeapObject.__init__(self, heap, map, address)
693 self.source = self.ObjectField(Script.SOURCE_OFFSET)
694 self.name = self.ObjectField(Script.NAME_OFFSET)
695
696
697class Code(HeapObject):
698 INSTRUCTION_SIZE_OFFSET = 4
699 ENTRY_OFFSET = 32
700
701 def __init__(self, heap, map, address):
702 HeapObject.__init__(self, heap, map, address)
703 self.entry = self.address + Code.ENTRY_OFFSET
704 self.instruction_size = \
705 heap.reader.ReadU32(self.address + Code.INSTRUCTION_SIZE_OFFSET)
706
707 def Print(self, p):
708 lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
709 p.Print("Code(%08x) {" % self.address)
710 p.Indent()
711 p.Print("instruction_size: %d" % self.instruction_size)
712 p.PrintLines(self._FormatLine(line) for line in lines)
713 p.Dedent()
714 p.Print("}")
715
716 def _FormatLine(self, line):
717 return FormatDisasmLine(self.entry, self.heap, line)
718
719
720class V8Heap(object):
721 CLASS_MAP = {
722 "SYMBOL_TYPE": SeqString,
723 "ASCII_SYMBOL_TYPE": SeqString,
724 "CONS_SYMBOL_TYPE": ConsString,
725 "CONS_ASCII_SYMBOL_TYPE": ConsString,
726 "EXTERNAL_SYMBOL_TYPE": ExternalString,
727 "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
728 "EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
729 "STRING_TYPE": SeqString,
730 "ASCII_STRING_TYPE": SeqString,
731 "CONS_STRING_TYPE": ConsString,
732 "CONS_ASCII_STRING_TYPE": ConsString,
733 "EXTERNAL_STRING_TYPE": ExternalString,
734 "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE": ExternalString,
735 "EXTERNAL_ASCII_STRING_TYPE": ExternalString,
736
737 "MAP_TYPE": Map,
738 "ODDBALL_TYPE": Oddball,
739 "FIXED_ARRAY_TYPE": FixedArray,
740 "JS_FUNCTION_TYPE": JSFunction,
741 "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
742 "SCRIPT_TYPE": Script,
743 "CODE_TYPE": Code
744 }
745
746 def __init__(self, reader, stack_map):
747 self.reader = reader
748 self.stack_map = stack_map
749 self.objects = {}
750
751 def FindObjectOrSmi(self, tagged_address):
752 if (tagged_address & 1) == 0: return tagged_address / 2
753 return self.FindObject(tagged_address)
754
755 def FindObject(self, tagged_address):
756 if tagged_address in self.objects:
757 return self.objects[tagged_address]
758 if (tagged_address & 1) != 1: return None
759 address = tagged_address - 1
760 if not self.reader.IsValidAddress(address): return None
761 map_tagged_address = self.reader.ReadU32(address)
762 if tagged_address == map_tagged_address:
763 # Meta map?
764 meta_map = Map(self, None, address)
765 instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
766 if instance_type_name != "MAP_TYPE": return None
767 meta_map.map = meta_map
768 object = meta_map
769 else:
770 map = self.FindObject(map_tagged_address)
771 if map is None: return None
772 instance_type_name = INSTANCE_TYPES.get(map.instance_type)
773 if instance_type_name is None: return None
774 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
775 object = cls(self, map, address)
776 self.objects[tagged_address] = object
777 return object
778
779
780EIP_PROXIMITY = 64
781
782
783def AnalyzeMinidump(options, minidump_name):
784 reader = MinidumpReader(options, minidump_name)
785 DebugPrint("========================================")
786 if reader.exception is None:
787 print "Minidump has no exception info"
788 return
789 print "Exception info:"
790 exception_thread = reader.thread_map[reader.exception.thread_id]
791 print " thread id: %d" % exception_thread.id
792 print " code: %08X" % reader.exception.exception.code
793 print " context:"
794 print " eax: %08x" % reader.exception_context.eax
795 print " ebx: %08x" % reader.exception_context.ebx
796 print " ecx: %08x" % reader.exception_context.ecx
797 print " edx: %08x" % reader.exception_context.edx
798 print " edi: %08x" % reader.exception_context.edi
799 print " esi: %08x" % reader.exception_context.esi
800 print " ebp: %08x" % reader.exception_context.ebp
801 print " esp: %08x" % reader.exception_context.esp
802 print " eip: %08x" % reader.exception_context.eip
803 # TODO(vitalyr): decode eflags.
804 print " eflags: %s" % bin(reader.exception_context.eflags)[2:]
805 print
806
807 stack_bottom = exception_thread.stack.start + \
808 exception_thread.stack.memory.data_size
809 stack_map = {reader.exception_context.eip: -1}
810 for slot in xrange(reader.exception_context.esp, stack_bottom, 4):
811 maybe_address = reader.ReadU32(slot)
812 if not maybe_address in stack_map:
813 stack_map[maybe_address] = slot
814 heap = V8Heap(reader, stack_map)
815
816 print "Disassembly around exception.eip:"
817 start = reader.exception_context.eip - EIP_PROXIMITY
818 lines = reader.GetDisasmLines(start, 2 * EIP_PROXIMITY)
819 for line in lines:
820 print FormatDisasmLine(start, heap, line)
821 print
822
823 print "Annotated stack (from exception.esp to bottom):"
824 for slot in xrange(reader.exception_context.esp, stack_bottom, 4):
825 maybe_address = reader.ReadU32(slot)
826 heap_object = heap.FindObject(maybe_address)
827 print "%08x: %08x" % (slot, maybe_address)
828 if heap_object:
829 heap_object.Print(Printer())
830 print
831
832 reader.Dispose()
833
834
835if __name__ == "__main__":
836 parser = optparse.OptionParser(USAGE)
837 options, args = parser.parse_args()
838 if len(args) != 1:
839 parser.print_help()
840 sys.exit(1)
841 AnalyzeMinidump(options, args[0])