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