Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1540)

Side by Side Diff: tools/grokdump.py

Issue 6312058: grokdump: Simple windows minidump analysis on linux. (Closed)
Patch Set: . Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/disasm.py ('k') | tools/ll_prof.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/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
30 import ctypes
31 import mmap
32 import optparse
33 import os
34 import disasm
35 import sys
36 import types
37 import codecs
38 import re
39
40
41 USAGE="""usage: %prog [OPTION]...
42
43 Minidump analyzer.
44
45 Shows the processor state at the point of exception including the
46 stack of the active thread and the referenced objects in the V8
47 heap. Code objects are disassembled and the addresses linked from the
48 stack (pushed return addresses) are marked with "=>".
49
50
51 Examples:
52 $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp
53 """
54
55 DEBUG=False
56
57
58 def DebugPrint(s):
59 if not DEBUG: return
60 print s
61
62
63 class 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
111 MINIDUMP_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
121 MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
122 ("data_size", ctypes.c_uint32),
123 ("rva", ctypes.c_uint32)
124 ])
125
126 MINIDUMP_DIRECTORY = Descriptor([
127 ("stream_type", ctypes.c_uint32),
128 ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
129 ])
130
131 MD_EXCEPTION_MAXIMUM_PARAMETERS = 15
132
133 MINIDUMP_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
143 MINIDUMP_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.
151 MD_UNUSED_STREAM = 0
152 MD_RESERVED_STREAM_0 = 1
153 MD_RESERVED_STREAM_1 = 2
154 MD_THREAD_LIST_STREAM = 3
155 MD_MODULE_LIST_STREAM = 4
156 MD_MEMORY_LIST_STREAM = 5
157 MD_EXCEPTION_STREAM = 6
158 MD_SYSTEM_INFO_STREAM = 7
159 MD_THREAD_EX_LIST_STREAM = 8
160 MD_MEMORY_64_LIST_STREAM = 9
161 MD_COMMENT_STREAM_A = 10
162 MD_COMMENT_STREAM_W = 11
163 MD_HANDLE_DATA_STREAM = 12
164 MD_FUNCTION_TABLE_STREAM = 13
165 MD_UNLOADED_MODULE_LIST_STREAM = 14
166 MD_MISC_INFO_STREAM = 15
167 MD_MEMORY_INFO_LIST_STREAM = 16
168 MD_THREAD_INFO_LIST_STREAM = 17
169 MD_HANDLE_OPERATION_LIST_STREAM = 18
170
171 MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80
172
173 MINIDUMP_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
185 MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512
186
187 # Context flags.
188 MD_CONTEXT_X86 = 0x00010000
189 MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001)
190 MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002)
191 MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004)
192 MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008)
193 MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010)
194 MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020)
195
196 def EnableOnFlag(type, flag):
197 return lambda o: [None, type][int((o.context_flags & flag) != 0)]
198
199 MINIDUMP_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
236 MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
237 ("start", ctypes.c_uint64),
238 ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
239 ])
240
241 MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
242 ("start", ctypes.c_uint64),
243 ("size", ctypes.c_uint64)
244 ])
245
246 MINIDUMP_MEMORY_LIST = Descriptor([
247 ("range_count", ctypes.c_uint32),
248 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
249 ])
250
251 MINIDUMP_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
257 MINIDUMP_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
267 MINIDUMP_THREAD_LIST = Descriptor([
268 ("thread_count", ctypes.c_uint32),
269 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
270 ])
271
272
273 class 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;
375 INSTANCE_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",
396 133: "PROXY_TYPE",
397 134: "BYTE_ARRAY_TYPE",
398 135: "PIXEL_ARRAY_TYPE",
399 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",
406 143: "FILLER_TYPE",
407 144: "ACCESSOR_INFO_TYPE",
408 145: "ACCESS_CHECK_INFO_TYPE",
409 146: "INTERCEPTOR_INFO_TYPE",
410 147: "CALL_HANDLER_INFO_TYPE",
411 148: "FUNCTION_TEMPLATE_INFO_TYPE",
412 149: "OBJECT_TEMPLATE_INFO_TYPE",
413 150: "SIGNATURE_INFO_TYPE",
414 151: "TYPE_SWITCH_INFO_TYPE",
415 152: "SCRIPT_TYPE",
416 153: "CODE_CACHE_TYPE",
417 156: "FIXED_ARRAY_TYPE",
418 157: "SHARED_FUNCTION_INFO_TYPE",
419 158: "JS_MESSAGE_OBJECT_TYPE",
420 159: "JS_VALUE_TYPE",
421 160: "JS_OBJECT_TYPE",
422 161: "JS_CONTEXT_EXTENSION_OBJECT_TYPE",
423 162: "JS_GLOBAL_OBJECT_TYPE",
424 163: "JS_BUILTINS_OBJECT_TYPE",
425 164: "JS_GLOBAL_PROXY_TYPE",
426 165: "JS_ARRAY_TYPE",
427 166: "JS_REGEXP_TYPE",
428 167: "JS_FUNCTION_TYPE",
429 154: "DEBUG_INFO_TYPE",
430 155: "BREAK_POINT_INFO_TYPE",
431 }
432
433
434 class Printer(object):
435 """Printer with indentation support."""
436
437 def __init__(self):
438 self.indent = 0
439
440 def Indent(self):
441 self.indent += 2
442
443 def Dedent(self):
444 self.indent -= 2
445
446 def Print(self, string):
447 print "%s%s" % (self._IndentString(), string)
448
449 def PrintLines(self, lines):
450 indent = self._IndentString()
451 print "\n".join("%s%s" % (indent, line) for line in lines)
452
453 def _IndentString(self):
454 return self.indent * " "
455
456
457 ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
458
459
460 def FormatDisasmLine(start, heap, line):
461 line_address = start + line[0]
462 stack_slot = heap.stack_map.get(line_address)
463 marker = " "
464 if stack_slot:
465 marker = "=>"
466 code = AnnotateAddresses(heap, line[1])
467 return "%s%08x %08x: %s" % (marker, line_address, line[0], code)
468
469
470 def AnnotateAddresses(heap, line):
471 extra = []
472 for m in ADDRESS_RE.finditer(line):
473 maybe_address = int(m.group(0), 16)
474 object = heap.FindObject(maybe_address)
475 if not object: continue
476 extra.append(str(object))
477 if len(extra) == 0: return line
478 return "%s ;; %s" % (line, ", ".join(extra))
479
480
481 class HeapObject(object):
482 def __init__(self, heap, map, address):
483 self.heap = heap
484 self.map = map
485 self.address = address
486
487 def Is(self, cls):
488 return isinstance(self, cls)
489
490 def Print(self, p):
491 p.Print(str(self))
492
493 def __str__(self):
494 return "HeapObject(%08x, %s)" % (self.address,
495 INSTANCE_TYPES[self.map.instance_type])
496
497 def ObjectField(self, offset):
498 field_value = self.heap.reader.ReadU32(self.address + offset)
499 return self.heap.FindObjectOrSmi(field_value)
500
501 def SmiField(self, offset):
502 field_value = self.heap.reader.ReadU32(self.address + offset)
503 assert (field_value & 1) == 0
504 return field_value / 2
505
506
507 class Map(HeapObject):
508 INSTANCE_TYPE_OFFSET = 8
509
510 def __init__(self, heap, map, address):
511 HeapObject.__init__(self, heap, map, address)
512 self.instance_type = \
513 heap.reader.ReadU8(self.address + Map.INSTANCE_TYPE_OFFSET)
514
515
516 class String(HeapObject):
517 LENGTH_OFFSET = 4
518
519 def __init__(self, heap, map, address):
520 HeapObject.__init__(self, heap, map, address)
521 self.length = self.SmiField(String.LENGTH_OFFSET)
522
523 def GetChars(self):
524 return "?string?"
525
526 def Print(self, p):
527 p.Print(str(self))
528
529 def __str__(self):
530 return "\"%s\"" % self.GetChars()
531
532
533 class SeqString(String):
534 CHARS_OFFSET = 12
535
536 def __init__(self, heap, map, address):
537 String.__init__(self, heap, map, address)
538 self.chars = heap.reader.ReadBytes(self.address + SeqString.CHARS_OFFSET,
539 self.length)
540
541 def GetChars(self):
542 return self.chars
543
544
545 class ExternalString(String):
546 RESOURCE_OFFSET = 12
547
548 WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
549 WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
550
551 def __init__(self, heap, map, address):
552 String.__init__(self, heap, map, address)
553 reader = heap.reader
554 self.resource = \
555 reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
556 self.chars = "?external string?"
557 if not reader.IsValidAddress(self.resource): return
558 string_impl_address = self.resource + \
559 ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
560 if not reader.IsValidAddress(string_impl_address): return
561 string_impl = reader.ReadU32(string_impl_address)
562 chars_ptr_address = string_impl + \
563 ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
564 if not reader.IsValidAddress(chars_ptr_address): return
565 chars_ptr = reader.ReadU32(chars_ptr_address)
566 if not reader.IsValidAddress(chars_ptr): return
567 raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
568 self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
569
570 def GetChars(self):
571 return self.chars
572
573
574 class ConsString(String):
575 LEFT_OFFSET = 12
576 RIGHT_OFFSET = 16
577
578 def __init__(self, heap, map, address):
579 String.__init__(self, heap, map, address)
580 self.left = self.ObjectField(ConsString.LEFT_OFFSET)
581 self.right = self.ObjectField(ConsString.RIGHT_OFFSET)
582
583 def GetChars(self):
584 return self.left.GetChars() + self.right.GetChars()
585
586
587 class Oddball(HeapObject):
588 TO_STRING_OFFSET = 4
589
590 def __init__(self, heap, map, address):
591 HeapObject.__init__(self, heap, map, address)
592 self.to_string = self.ObjectField(Oddball.TO_STRING_OFFSET)
593
594 def Print(self, p):
595 p.Print(str(self))
596
597 def __str__(self):
598 return "<%s>" % self.to_string.GetChars()
599
600
601 class FixedArray(HeapObject):
602 LENGTH_OFFSET = 4
603 ELEMENTS_OFFSET = 8
604
605 def __init__(self, heap, map, address):
606 HeapObject.__init__(self, heap, map, address)
607 self.length = self.SmiField(FixedArray.LENGTH_OFFSET)
608
609 def Print(self, p):
610 p.Print("FixedArray(%08x) {" % self.address)
611 p.Indent()
612 p.Print("length: %d" % self.length)
613 for i in xrange(self.length):
614 offset = FixedArray.ELEMENTS_OFFSET + 4 * i
615 p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
616 p.Dedent()
617 p.Print("}")
618
619 def __str__(self):
620 return "FixedArray(%08x, length=%d)" % (self.address, self.length)
621
622
623 class JSFunction(HeapObject):
624 CODE_ENTRY_OFFSET = 12
625 SHARED_OFFSET = 20
626
627 def __init__(self, heap, map, address):
628 HeapObject.__init__(self, heap, map, address)
629 code_entry = \
630 heap.reader.ReadU32(self.address + JSFunction.CODE_ENTRY_OFFSET)
631 self.code = heap.FindObject(code_entry - Code.ENTRY_OFFSET + 1)
632 self.shared = self.ObjectField(JSFunction.SHARED_OFFSET)
633
634 def Print(self, p):
635 source = "\n".join(" %s" % line for line in self._GetSource().split("\n"))
636 p.Print("JSFunction(%08x) {" % self.address)
637 p.Indent()
638 p.Print("inferred name: %s" % self.shared.inferred_name)
639 if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
640 p.Print("script name: %s" % self.shared.script.name)
641 p.Print("source:")
642 p.PrintLines(self._GetSource().split("\n"))
643 p.Print("code:")
644 self.code.Print(p)
645 if self.code != self.shared.code:
646 p.Print("unoptimized code:")
647 self.shared.code.Print(p)
648 p.Dedent()
649 p.Print("}")
650
651 def __str__(self):
652 inferred_name = ""
653 if self.shared.Is(SharedFunctionInfo):
654 inferred_name = self.shared.inferred_name
655 return "JSFunction(%08x, %s)" % (self.address, inferred_name)
656
657 def _GetSource(self):
658 source = "?source?"
659 start = self.shared.start_position
660 end = self.shared.end_position
661 if not self.shared.script.Is(Script): return source
662 script_source = self.shared.script.source
663 if not script_source.Is(String): return source
664 return script_source.GetChars()[start:end]
665
666
667 class SharedFunctionInfo(HeapObject):
668 CODE_OFFSET = 2 * 4
669 SCRIPT_OFFSET = 7 * 4
670 INFERRED_NAME_OFFSET = 9 * 4
671 START_POSITION_AND_TYPE_OFFSET = 17 * 4
672 END_POSITION_OFFSET = 18 * 4
673
674 def __init__(self, heap, map, address):
675 HeapObject.__init__(self, heap, map, address)
676 self.code = self.ObjectField(SharedFunctionInfo.CODE_OFFSET)
677 self.script = self.ObjectField(SharedFunctionInfo.SCRIPT_OFFSET)
678 self.inferred_name = \
679 self.ObjectField(SharedFunctionInfo.INFERRED_NAME_OFFSET)
680 start_position_and_type = \
681 self.SmiField(SharedFunctionInfo.START_POSITION_AND_TYPE_OFFSET)
682 self.start_position = start_position_and_type >> 2
683 self.end_position = self.SmiField(SharedFunctionInfo.END_POSITION_OFFSET)
684
685
686 class Script(HeapObject):
687 SOURCE_OFFSET = 4
688 NAME_OFFSET = 8
689
690 def __init__(self, heap, map, address):
691 HeapObject.__init__(self, heap, map, address)
692 self.source = self.ObjectField(Script.SOURCE_OFFSET)
693 self.name = self.ObjectField(Script.NAME_OFFSET)
694
695
696 class Code(HeapObject):
697 INSTRUCTION_SIZE_OFFSET = 4
698 ENTRY_OFFSET = 32
699
700 def __init__(self, heap, map, address):
701 HeapObject.__init__(self, heap, map, address)
702 self.entry = self.address + Code.ENTRY_OFFSET
703 self.instruction_size = \
704 heap.reader.ReadU32(self.address + Code.INSTRUCTION_SIZE_OFFSET)
705
706 def Print(self, p):
707 lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
708 p.Print("Code(%08x) {" % self.address)
709 p.Indent()
710 p.Print("instruction_size: %d" % self.instruction_size)
711 p.PrintLines(self._FormatLine(line) for line in lines)
712 p.Dedent()
713 p.Print("}")
714
715 def _FormatLine(self, line):
716 return FormatDisasmLine(self.entry, self.heap, line)
717
718
719 class V8Heap(object):
720 CLASS_MAP = {
721 "SYMBOL_TYPE": SeqString,
722 "ASCII_SYMBOL_TYPE": SeqString,
723 "CONS_SYMBOL_TYPE": ConsString,
724 "CONS_ASCII_SYMBOL_TYPE": ConsString,
725 "EXTERNAL_SYMBOL_TYPE": ExternalString,
726 "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
727 "EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
728 "STRING_TYPE": SeqString,
729 "ASCII_STRING_TYPE": SeqString,
730 "CONS_STRING_TYPE": ConsString,
731 "CONS_ASCII_STRING_TYPE": ConsString,
732 "EXTERNAL_STRING_TYPE": ExternalString,
733 "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE": ExternalString,
734 "EXTERNAL_ASCII_STRING_TYPE": ExternalString,
735
736 "MAP_TYPE": Map,
737 "ODDBALL_TYPE": Oddball,
738 "FIXED_ARRAY_TYPE": FixedArray,
739 "JS_FUNCTION_TYPE": JSFunction,
740 "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
741 "SCRIPT_TYPE": Script,
742 "CODE_TYPE": Code
743 }
744
745 def __init__(self, reader, stack_map):
746 self.reader = reader
747 self.stack_map = stack_map
748 self.objects = {}
749
750 def FindObjectOrSmi(self, tagged_address):
751 if (tagged_address & 1) == 0: return tagged_address / 2
752 return self.FindObject(tagged_address)
753
754 def FindObject(self, tagged_address):
755 if tagged_address in self.objects:
756 return self.objects[tagged_address]
757 if (tagged_address & 1) != 1: return None
758 address = tagged_address - 1
759 if not self.reader.IsValidAddress(address): return None
760 map_tagged_address = self.reader.ReadU32(address)
761 if tagged_address == map_tagged_address:
762 # Meta map?
763 meta_map = Map(self, None, address)
764 instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
765 if instance_type_name != "MAP_TYPE": return None
766 meta_map.map = meta_map
767 object = meta_map
768 else:
769 map = self.FindObject(map_tagged_address)
770 if map is None: return None
771 instance_type_name = INSTANCE_TYPES.get(map.instance_type)
772 if instance_type_name is None: return None
773 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
774 object = cls(self, map, address)
775 self.objects[tagged_address] = object
776 return object
777
778
779 EIP_PROXIMITY = 64
780
781
782 def AnalyzeMinidump(options, minidump_name):
783 reader = MinidumpReader(options, minidump_name)
784 DebugPrint("========================================")
785 if reader.exception is None:
786 print "Minidump has no exception info"
787 return
788 print "Exception info:"
789 exception_thread = reader.thread_map[reader.exception.thread_id]
790 print " thread id: %d" % exception_thread.id
791 print " code: %08X" % reader.exception.exception.code
792 print " context:"
793 print " eax: %08x" % reader.exception_context.eax
794 print " ebx: %08x" % reader.exception_context.ebx
795 print " ecx: %08x" % reader.exception_context.ecx
796 print " edx: %08x" % reader.exception_context.edx
797 print " edi: %08x" % reader.exception_context.edi
798 print " esi: %08x" % reader.exception_context.esi
799 print " ebp: %08x" % reader.exception_context.ebp
800 print " esp: %08x" % reader.exception_context.esp
801 print " eip: %08x" % reader.exception_context.eip
802 # TODO(vitalyr): decode eflags.
803 print " eflags: %s" % bin(reader.exception_context.eflags)[2:]
804 print
805
806 stack_bottom = exception_thread.stack.start + \
807 exception_thread.stack.memory.data_size
808 stack_map = {reader.exception_context.eip: -1}
809 for slot in xrange(reader.exception_context.esp, stack_bottom, 4):
810 maybe_address = reader.ReadU32(slot)
811 if not maybe_address in stack_map:
812 stack_map[maybe_address] = slot
813 heap = V8Heap(reader, stack_map)
814
815 print "Disassembly around exception.eip:"
816 start = reader.exception_context.eip - EIP_PROXIMITY
817 lines = reader.GetDisasmLines(start, 2 * EIP_PROXIMITY)
818 for line in lines:
819 print FormatDisasmLine(start, heap, line)
820 print
821
822 print "Annotated stack (from exception.esp to bottom):"
823 for slot in xrange(reader.exception_context.esp, stack_bottom, 4):
824 maybe_address = reader.ReadU32(slot)
825 heap_object = heap.FindObject(maybe_address)
826 print "%08x: %08x" % (slot, maybe_address)
827 if heap_object:
828 heap_object.Print(Printer())
829 print
830
831 reader.Dispose()
832
833
834 if __name__ == "__main__":
835 parser = optparse.OptionParser(USAGE)
836 options, args = parser.parse_args()
837 if len(args) != 1:
838 parser.print_help()
839 sys.exit(1)
840 AnalyzeMinidump(options, args[0])
OLDNEW
« no previous file with comments | « tools/disasm.py ('k') | tools/ll_prof.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698