- add third_party src.
[platform/framework/web/crosswalk.git] / src / v8 / tools / grokdump.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2012 the V8 project authors. All rights reserved.
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
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 bisect
31 import cmd
32 import codecs
33 import ctypes
34 import datetime
35 import disasm
36 import mmap
37 import optparse
38 import os
39 import re
40 import struct
41 import sys
42 import types
43 import v8heapconst
44
45 USAGE="""usage: %prog [OPTIONS] [DUMP-FILE]
46
47 Minidump analyzer.
48
49 Shows the processor state at the point of exception including the
50 stack of the active thread and the referenced objects in the V8
51 heap. Code objects are disassembled and the addresses linked from the
52 stack (e.g. pushed return addresses) are marked with "=>".
53
54 Examples:
55   $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp"""
56
57
58 DEBUG=False
59
60
61 def DebugPrint(s):
62   if not DEBUG: return
63   print s
64
65
66 class Descriptor(object):
67   """Descriptor of a structure in a memory."""
68
69   def __init__(self, fields):
70     self.fields = fields
71     self.is_flexible = False
72     for _, type_or_func in fields:
73       if isinstance(type_or_func, types.FunctionType):
74         self.is_flexible = True
75         break
76     if not self.is_flexible:
77       self.ctype = Descriptor._GetCtype(fields)
78       self.size = ctypes.sizeof(self.ctype)
79
80   def Read(self, memory, offset):
81     if self.is_flexible:
82       fields_copy = self.fields[:]
83       last = 0
84       for name, type_or_func in fields_copy:
85         if isinstance(type_or_func, types.FunctionType):
86           partial_ctype = Descriptor._GetCtype(fields_copy[:last])
87           partial_object = partial_ctype.from_buffer(memory, offset)
88           type = type_or_func(partial_object)
89           if type is not None:
90             fields_copy[last] = (name, type)
91             last += 1
92         else:
93           last += 1
94       complete_ctype = Descriptor._GetCtype(fields_copy[:last])
95     else:
96       complete_ctype = self.ctype
97     return complete_ctype.from_buffer(memory, offset)
98
99   @staticmethod
100   def _GetCtype(fields):
101     class Raw(ctypes.Structure):
102       _fields_ = fields
103       _pack_ = 1
104
105       def __str__(self):
106         return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field))
107                                for field, _ in Raw._fields_) + "}"
108     return Raw
109
110
111 def FullDump(reader, heap):
112   """Dump all available memory regions."""
113   def dump_region(reader, start, size, location):
114     print
115     while start & 3 != 0:
116       start += 1
117       size -= 1
118       location += 1
119     is_executable = reader.IsProbableExecutableRegion(location, size)
120     is_ascii = reader.IsProbableASCIIRegion(location, size)
121
122     if is_executable is not False:
123       lines = reader.GetDisasmLines(start, size)
124       for line in lines:
125         print FormatDisasmLine(start, heap, line)
126       print
127
128     if is_ascii is not False:
129       # Output in the same format as the Unix hd command
130       addr = start
131       for slot in xrange(location, location + size, 16):
132         hex_line = ""
133         asc_line = ""
134         for i in xrange(0, 16):
135           if slot + i < location + size:
136             byte = ctypes.c_uint8.from_buffer(reader.minidump, slot + i).value
137             if byte >= 0x20 and byte < 0x7f:
138               asc_line += chr(byte)
139             else:
140               asc_line += "."
141             hex_line += " %02x" % (byte)
142           else:
143             hex_line += "   "
144           if i == 7:
145             hex_line += " "
146         print "%s  %s |%s|" % (reader.FormatIntPtr(addr),
147                                hex_line,
148                                asc_line)
149         addr += 16
150
151     if is_executable is not True and is_ascii is not True:
152       print "%s - %s" % (reader.FormatIntPtr(start),
153                          reader.FormatIntPtr(start + size))
154       for slot in xrange(start,
155                          start + size,
156                          reader.PointerSize()):
157         maybe_address = reader.ReadUIntPtr(slot)
158         heap_object = heap.FindObject(maybe_address)
159         print "%s: %s" % (reader.FormatIntPtr(slot),
160                           reader.FormatIntPtr(maybe_address))
161         if heap_object:
162           heap_object.Print(Printer())
163           print
164
165   reader.ForEachMemoryRegion(dump_region)
166
167 # Heap constants generated by 'make grokdump' in v8heapconst module.
168 INSTANCE_TYPES = v8heapconst.INSTANCE_TYPES
169 KNOWN_MAPS = v8heapconst.KNOWN_MAPS
170 KNOWN_OBJECTS = v8heapconst.KNOWN_OBJECTS
171
172 # Set of structures and constants that describe the layout of minidump
173 # files. Based on MSDN and Google Breakpad.
174
175 MINIDUMP_HEADER = Descriptor([
176   ("signature", ctypes.c_uint32),
177   ("version", ctypes.c_uint32),
178   ("stream_count", ctypes.c_uint32),
179   ("stream_directories_rva", ctypes.c_uint32),
180   ("checksum", ctypes.c_uint32),
181   ("time_date_stampt", ctypes.c_uint32),
182   ("flags", ctypes.c_uint64)
183 ])
184
185 MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
186   ("data_size", ctypes.c_uint32),
187   ("rva", ctypes.c_uint32)
188 ])
189
190 MINIDUMP_STRING = Descriptor([
191   ("length", ctypes.c_uint32),
192   ("buffer", lambda t: ctypes.c_uint8 * (t.length + 2))
193 ])
194
195 MINIDUMP_DIRECTORY = Descriptor([
196   ("stream_type", ctypes.c_uint32),
197   ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
198 ])
199
200 MD_EXCEPTION_MAXIMUM_PARAMETERS = 15
201
202 MINIDUMP_EXCEPTION = Descriptor([
203   ("code", ctypes.c_uint32),
204   ("flags", ctypes.c_uint32),
205   ("record", ctypes.c_uint64),
206   ("address", ctypes.c_uint64),
207   ("parameter_count", ctypes.c_uint32),
208   ("unused_alignment", ctypes.c_uint32),
209   ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS)
210 ])
211
212 MINIDUMP_EXCEPTION_STREAM = Descriptor([
213   ("thread_id", ctypes.c_uint32),
214   ("unused_alignment", ctypes.c_uint32),
215   ("exception", MINIDUMP_EXCEPTION.ctype),
216   ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
217 ])
218
219 # Stream types.
220 MD_UNUSED_STREAM = 0
221 MD_RESERVED_STREAM_0 = 1
222 MD_RESERVED_STREAM_1 = 2
223 MD_THREAD_LIST_STREAM = 3
224 MD_MODULE_LIST_STREAM = 4
225 MD_MEMORY_LIST_STREAM = 5
226 MD_EXCEPTION_STREAM = 6
227 MD_SYSTEM_INFO_STREAM = 7
228 MD_THREAD_EX_LIST_STREAM = 8
229 MD_MEMORY_64_LIST_STREAM = 9
230 MD_COMMENT_STREAM_A = 10
231 MD_COMMENT_STREAM_W = 11
232 MD_HANDLE_DATA_STREAM = 12
233 MD_FUNCTION_TABLE_STREAM = 13
234 MD_UNLOADED_MODULE_LIST_STREAM = 14
235 MD_MISC_INFO_STREAM = 15
236 MD_MEMORY_INFO_LIST_STREAM = 16
237 MD_THREAD_INFO_LIST_STREAM = 17
238 MD_HANDLE_OPERATION_LIST_STREAM = 18
239
240 MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80
241
242 MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([
243   ("control_word", ctypes.c_uint32),
244   ("status_word", ctypes.c_uint32),
245   ("tag_word", ctypes.c_uint32),
246   ("error_offset", ctypes.c_uint32),
247   ("error_selector", ctypes.c_uint32),
248   ("data_offset", ctypes.c_uint32),
249   ("data_selector", ctypes.c_uint32),
250   ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE),
251   ("cr0_npx_state", ctypes.c_uint32)
252 ])
253
254 MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512
255
256 # Context flags.
257 MD_CONTEXT_X86 = 0x00010000
258 MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001)
259 MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002)
260 MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004)
261 MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008)
262 MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010)
263 MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020)
264
265 def EnableOnFlag(type, flag):
266   return lambda o: [None, type][int((o.context_flags & flag) != 0)]
267
268 MINIDUMP_CONTEXT_X86 = Descriptor([
269   ("context_flags", ctypes.c_uint32),
270   # MD_CONTEXT_X86_DEBUG_REGISTERS.
271   ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
272   ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
273   ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
274   ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
275   ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
276   ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
277   # MD_CONTEXT_X86_FLOATING_POINT.
278   ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype,
279                               MD_CONTEXT_X86_FLOATING_POINT)),
280   # MD_CONTEXT_X86_SEGMENTS.
281   ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
282   ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
283   ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
284   ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
285   # MD_CONTEXT_X86_INTEGER.
286   ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
287   ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
288   ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
289   ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
290   ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
291   ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
292   # MD_CONTEXT_X86_CONTROL.
293   ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
294   ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
295   ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
296   ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
297   ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
298   ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
299   # MD_CONTEXT_X86_EXTENDED_REGISTERS.
300   ("extended_registers",
301    EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE,
302                 MD_CONTEXT_X86_EXTENDED_REGISTERS))
303 ])
304
305 MD_CONTEXT_ARM = 0x40000000
306 MD_CONTEXT_ARM_INTEGER = (MD_CONTEXT_ARM | 0x00000002)
307 MD_CONTEXT_ARM_FLOATING_POINT = (MD_CONTEXT_ARM | 0x00000004)
308 MD_FLOATINGSAVEAREA_ARM_FPR_COUNT = 32
309 MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT = 8
310
311 MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([
312   ("fpscr", ctypes.c_uint64),
313   ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPR_COUNT),
314   ("extra", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT)
315 ])
316
317 MINIDUMP_CONTEXT_ARM = Descriptor([
318   ("context_flags", ctypes.c_uint32),
319   # MD_CONTEXT_ARM_INTEGER.
320   ("r0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
321   ("r1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
322   ("r2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
323   ("r3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
324   ("r4", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
325   ("r5", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
326   ("r6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
327   ("r7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
328   ("r8", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
329   ("r9", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
330   ("r10", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
331   ("r11", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
332   ("r12", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
333   ("sp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
334   ("lr", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
335   ("pc", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
336   ("cpsr", ctypes.c_uint32),
337   ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype,
338                               MD_CONTEXT_ARM_FLOATING_POINT))
339 ])
340
341 MD_CONTEXT_AMD64 = 0x00100000
342 MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001)
343 MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002)
344 MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004)
345 MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008)
346 MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010)
347
348 MINIDUMP_CONTEXT_AMD64 = Descriptor([
349   ("p1_home", ctypes.c_uint64),
350   ("p2_home", ctypes.c_uint64),
351   ("p3_home", ctypes.c_uint64),
352   ("p4_home", ctypes.c_uint64),
353   ("p5_home", ctypes.c_uint64),
354   ("p6_home", ctypes.c_uint64),
355   ("context_flags", ctypes.c_uint32),
356   ("mx_csr", ctypes.c_uint32),
357   # MD_CONTEXT_AMD64_CONTROL.
358   ("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
359   # MD_CONTEXT_AMD64_SEGMENTS
360   ("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
361   ("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
362   ("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
363   ("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
364   # MD_CONTEXT_AMD64_CONTROL.
365   ("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
366   ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)),
367   # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
368   ("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
369   ("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
370   ("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
371   ("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
372   ("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
373   ("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
374   # MD_CONTEXT_AMD64_INTEGER.
375   ("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
376   ("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
377   ("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
378   ("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
379   # MD_CONTEXT_AMD64_CONTROL.
380   ("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
381   # MD_CONTEXT_AMD64_INTEGER.
382   ("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
383   ("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
384   ("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
385   ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
386   ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
387   ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
388   ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
389   ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
390   ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
391   ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
392   ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
393   # MD_CONTEXT_AMD64_CONTROL.
394   ("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
395   # MD_CONTEXT_AMD64_FLOATING_POINT
396   ("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
397                                  MD_CONTEXT_AMD64_FLOATING_POINT)),
398   ("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
399                                     MD_CONTEXT_AMD64_FLOATING_POINT)),
400   ("vector_control", EnableOnFlag(ctypes.c_uint64,
401                                   MD_CONTEXT_AMD64_FLOATING_POINT)),
402   # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
403   ("debug_control", EnableOnFlag(ctypes.c_uint64,
404                                  MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
405   ("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64,
406                                       MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
407   ("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64,
408                                         MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
409   ("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64,
410                                          MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
411   ("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64,
412                                            MD_CONTEXT_AMD64_DEBUG_REGISTERS))
413 ])
414
415 MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
416   ("start", ctypes.c_uint64),
417   ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
418 ])
419
420 MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
421   ("start", ctypes.c_uint64),
422   ("size", ctypes.c_uint64)
423 ])
424
425 MINIDUMP_MEMORY_LIST = Descriptor([
426   ("range_count", ctypes.c_uint32),
427   ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
428 ])
429
430 MINIDUMP_MEMORY_LIST64 = Descriptor([
431   ("range_count", ctypes.c_uint64),
432   ("base_rva", ctypes.c_uint64),
433   ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count)
434 ])
435
436 MINIDUMP_THREAD = Descriptor([
437   ("id", ctypes.c_uint32),
438   ("suspend_count", ctypes.c_uint32),
439   ("priority_class", ctypes.c_uint32),
440   ("priority", ctypes.c_uint32),
441   ("ted", ctypes.c_uint64),
442   ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype),
443   ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
444 ])
445
446 MINIDUMP_THREAD_LIST = Descriptor([
447   ("thread_count", ctypes.c_uint32),
448   ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
449 ])
450
451 MINIDUMP_VS_FIXEDFILEINFO = Descriptor([
452   ("dwSignature", ctypes.c_uint32),
453   ("dwStrucVersion", ctypes.c_uint32),
454   ("dwFileVersionMS", ctypes.c_uint32),
455   ("dwFileVersionLS", ctypes.c_uint32),
456   ("dwProductVersionMS", ctypes.c_uint32),
457   ("dwProductVersionLS", ctypes.c_uint32),
458   ("dwFileFlagsMask", ctypes.c_uint32),
459   ("dwFileFlags", ctypes.c_uint32),
460   ("dwFileOS", ctypes.c_uint32),
461   ("dwFileType", ctypes.c_uint32),
462   ("dwFileSubtype", ctypes.c_uint32),
463   ("dwFileDateMS", ctypes.c_uint32),
464   ("dwFileDateLS", ctypes.c_uint32)
465 ])
466
467 MINIDUMP_RAW_MODULE = Descriptor([
468   ("base_of_image", ctypes.c_uint64),
469   ("size_of_image", ctypes.c_uint32),
470   ("checksum", ctypes.c_uint32),
471   ("time_date_stamp", ctypes.c_uint32),
472   ("module_name_rva", ctypes.c_uint32),
473   ("version_info", MINIDUMP_VS_FIXEDFILEINFO.ctype),
474   ("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
475   ("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
476   ("reserved0", ctypes.c_uint32 * 2),
477   ("reserved1", ctypes.c_uint32 * 2)
478 ])
479
480 MINIDUMP_MODULE_LIST = Descriptor([
481   ("number_of_modules", ctypes.c_uint32),
482   ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules)
483 ])
484
485 MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
486   ("processor_architecture", ctypes.c_uint16)
487 ])
488
489 MD_CPU_ARCHITECTURE_X86 = 0
490 MD_CPU_ARCHITECTURE_ARM = 5
491 MD_CPU_ARCHITECTURE_AMD64 = 9
492
493 class FuncSymbol:
494   def __init__(self, start, size, name):
495     self.start = start
496     self.end = self.start + size
497     self.name = name
498
499   def __cmp__(self, other):
500     if isinstance(other, FuncSymbol):
501       return self.start - other.start
502     return self.start - other
503
504   def Covers(self, addr):
505     return (self.start <= addr) and (addr < self.end)
506
507 class MinidumpReader(object):
508   """Minidump (.dmp) reader."""
509
510   _HEADER_MAGIC = 0x504d444d
511
512   def __init__(self, options, minidump_name):
513     self.minidump_name = minidump_name
514     self.minidump_file = open(minidump_name, "r")
515     self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE)
516     self.header = MINIDUMP_HEADER.Read(self.minidump, 0)
517     if self.header.signature != MinidumpReader._HEADER_MAGIC:
518       print >>sys.stderr, "Warning: Unsupported minidump header magic!"
519     DebugPrint(self.header)
520     directories = []
521     offset = self.header.stream_directories_rva
522     for _ in xrange(self.header.stream_count):
523       directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
524       offset += MINIDUMP_DIRECTORY.size
525     self.arch = None
526     self.exception = None
527     self.exception_context = None
528     self.memory_list = None
529     self.memory_list64 = None
530     self.module_list = None
531     self.thread_map = {}
532
533     self.symdir = options.symdir
534     self.modules_with_symbols = []
535     self.symbols = []
536
537     # Find MDRawSystemInfo stream and determine arch.
538     for d in directories:
539       if d.stream_type == MD_SYSTEM_INFO_STREAM:
540         system_info = MINIDUMP_RAW_SYSTEM_INFO.Read(
541             self.minidump, d.location.rva)
542         self.arch = system_info.processor_architecture
543         assert self.arch in [MD_CPU_ARCHITECTURE_AMD64,
544                              MD_CPU_ARCHITECTURE_ARM,
545                              MD_CPU_ARCHITECTURE_X86]
546     assert not self.arch is None
547
548     for d in directories:
549       DebugPrint(d)
550       if d.stream_type == MD_EXCEPTION_STREAM:
551         self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
552           self.minidump, d.location.rva)
553         DebugPrint(self.exception)
554         if self.arch == MD_CPU_ARCHITECTURE_X86:
555           self.exception_context = MINIDUMP_CONTEXT_X86.Read(
556               self.minidump, self.exception.thread_context.rva)
557         elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
558           self.exception_context = MINIDUMP_CONTEXT_AMD64.Read(
559               self.minidump, self.exception.thread_context.rva)
560         elif self.arch == MD_CPU_ARCHITECTURE_ARM:
561           self.exception_context = MINIDUMP_CONTEXT_ARM.Read(
562               self.minidump, self.exception.thread_context.rva)
563         DebugPrint(self.exception_context)
564       elif d.stream_type == MD_THREAD_LIST_STREAM:
565         thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
566         assert ctypes.sizeof(thread_list) == d.location.data_size
567         DebugPrint(thread_list)
568         for thread in thread_list.threads:
569           DebugPrint(thread)
570           self.thread_map[thread.id] = thread
571       elif d.stream_type == MD_MODULE_LIST_STREAM:
572         assert self.module_list is None
573         self.module_list = MINIDUMP_MODULE_LIST.Read(
574           self.minidump, d.location.rva)
575         assert ctypes.sizeof(self.module_list) == d.location.data_size
576       elif d.stream_type == MD_MEMORY_LIST_STREAM:
577         print >>sys.stderr, "Warning: This is not a full minidump!"
578         assert self.memory_list is None
579         self.memory_list = MINIDUMP_MEMORY_LIST.Read(
580           self.minidump, d.location.rva)
581         assert ctypes.sizeof(self.memory_list) == d.location.data_size
582         DebugPrint(self.memory_list)
583       elif d.stream_type == MD_MEMORY_64_LIST_STREAM:
584         assert self.memory_list64 is None
585         self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read(
586           self.minidump, d.location.rva)
587         assert ctypes.sizeof(self.memory_list64) == d.location.data_size
588         DebugPrint(self.memory_list64)
589
590   def IsValidAddress(self, address):
591     return self.FindLocation(address) is not None
592
593   def ReadU8(self, address):
594     location = self.FindLocation(address)
595     return ctypes.c_uint8.from_buffer(self.minidump, location).value
596
597   def ReadU32(self, address):
598     location = self.FindLocation(address)
599     return ctypes.c_uint32.from_buffer(self.minidump, location).value
600
601   def ReadU64(self, address):
602     location = self.FindLocation(address)
603     return ctypes.c_uint64.from_buffer(self.minidump, location).value
604
605   def ReadUIntPtr(self, address):
606     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
607       return self.ReadU64(address)
608     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
609       return self.ReadU32(address)
610     elif self.arch == MD_CPU_ARCHITECTURE_X86:
611       return self.ReadU32(address)
612
613   def ReadBytes(self, address, size):
614     location = self.FindLocation(address)
615     return self.minidump[location:location + size]
616
617   def _ReadWord(self, location):
618     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
619       return ctypes.c_uint64.from_buffer(self.minidump, location).value
620     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
621       return ctypes.c_uint32.from_buffer(self.minidump, location).value
622     elif self.arch == MD_CPU_ARCHITECTURE_X86:
623       return ctypes.c_uint32.from_buffer(self.minidump, location).value
624
625   def IsProbableASCIIRegion(self, location, length):
626     ascii_bytes = 0
627     non_ascii_bytes = 0
628     for loc in xrange(location, location + length):
629       byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
630       if byte >= 0x7f:
631         non_ascii_bytes += 1
632       if byte < 0x20 and byte != 0:
633         non_ascii_bytes += 1
634       if byte < 0x7f and byte >= 0x20:
635         ascii_bytes += 1
636       if byte == 0xa:  # newline
637         ascii_bytes += 1
638     if ascii_bytes * 10 <= length:
639       return False
640     if length > 0 and ascii_bytes > non_ascii_bytes * 7:
641       return True
642     if ascii_bytes > non_ascii_bytes * 3:
643       return None  # Maybe
644     return False
645
646   def IsProbableExecutableRegion(self, location, length):
647     opcode_bytes = 0
648     sixty_four = self.arch == MD_CPU_ARCHITECTURE_AMD64
649     for loc in xrange(location, location + length):
650       byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
651       if (byte == 0x8b or           # mov
652           byte == 0x89 or           # mov reg-reg
653           (byte & 0xf0) == 0x50 or  # push/pop
654           (sixty_four and (byte & 0xf0) == 0x40) or  # rex prefix
655           byte == 0xc3 or           # return
656           byte == 0x74 or           # jeq
657           byte == 0x84 or           # jeq far
658           byte == 0x75 or           # jne
659           byte == 0x85 or           # jne far
660           byte == 0xe8 or           # call
661           byte == 0xe9 or           # jmp far
662           byte == 0xeb):            # jmp near
663         opcode_bytes += 1
664     opcode_percent = (opcode_bytes * 100) / length
665     threshold = 20
666     if opcode_percent > threshold + 2:
667       return True
668     if opcode_percent > threshold - 2:
669       return None  # Maybe
670     return False
671
672   def FindRegion(self, addr):
673     answer = [-1, -1]
674     def is_in(reader, start, size, location):
675       if addr >= start and addr < start + size:
676         answer[0] = start
677         answer[1] = size
678     self.ForEachMemoryRegion(is_in)
679     if answer[0] == -1:
680       return None
681     return answer
682
683   def ForEachMemoryRegion(self, cb):
684     if self.memory_list64 is not None:
685       for r in self.memory_list64.ranges:
686         location = self.memory_list64.base_rva + offset
687         cb(self, r.start, r.size, location)
688         offset += r.size
689
690     if self.memory_list is not None:
691       for r in self.memory_list.ranges:
692         cb(self, r.start, r.memory.data_size, r.memory.rva)
693
694   def FindWord(self, word, alignment=0):
695     def search_inside_region(reader, start, size, location):
696       location = (location + alignment) & ~alignment
697       for loc in xrange(location, location + size - self.PointerSize()):
698         if reader._ReadWord(loc) == word:
699           slot = start + (loc - location)
700           print "%s: %s" % (reader.FormatIntPtr(slot),
701                             reader.FormatIntPtr(word))
702     self.ForEachMemoryRegion(search_inside_region)
703
704   def FindLocation(self, address):
705     offset = 0
706     if self.memory_list64 is not None:
707       for r in self.memory_list64.ranges:
708         if r.start <= address < r.start + r.size:
709           return self.memory_list64.base_rva + offset + address - r.start
710         offset += r.size
711     if self.memory_list is not None:
712       for r in self.memory_list.ranges:
713         if r.start <= address < r.start + r.memory.data_size:
714           return r.memory.rva + address - r.start
715     return None
716
717   def GetDisasmLines(self, address, size):
718     def CountUndefinedInstructions(lines):
719       pattern = "<UNDEFINED>"
720       return sum([line.count(pattern) for (ignore, line) in lines])
721
722     location = self.FindLocation(address)
723     if location is None: return []
724     arch = None
725     possible_objdump_flags = [""]
726     if self.arch == MD_CPU_ARCHITECTURE_X86:
727       arch = "ia32"
728     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
729       arch = "arm"
730       possible_objdump_flags = ["", "--disassembler-options=force-thumb"]
731     elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
732       arch = "x64"
733     results = [ disasm.GetDisasmLines(self.minidump_name,
734                                      location,
735                                      size,
736                                      arch,
737                                      False,
738                                      objdump_flags)
739                 for objdump_flags in possible_objdump_flags ]
740     return min(results, key=CountUndefinedInstructions)
741
742
743   def Dispose(self):
744     self.minidump.close()
745     self.minidump_file.close()
746
747   def ExceptionIP(self):
748     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
749       return self.exception_context.rip
750     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
751       return self.exception_context.pc
752     elif self.arch == MD_CPU_ARCHITECTURE_X86:
753       return self.exception_context.eip
754
755   def ExceptionSP(self):
756     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
757       return self.exception_context.rsp
758     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
759       return self.exception_context.sp
760     elif self.arch == MD_CPU_ARCHITECTURE_X86:
761       return self.exception_context.esp
762
763   def ExceptionFP(self):
764     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
765       return self.exception_context.rbp
766     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
767       return None
768     elif self.arch == MD_CPU_ARCHITECTURE_X86:
769       return self.exception_context.ebp
770
771   def FormatIntPtr(self, value):
772     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
773       return "%016x" % value
774     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
775       return "%08x" % value
776     elif self.arch == MD_CPU_ARCHITECTURE_X86:
777       return "%08x" % value
778
779   def PointerSize(self):
780     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
781       return 8
782     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
783       return 4
784     elif self.arch == MD_CPU_ARCHITECTURE_X86:
785       return 4
786
787   def Register(self, name):
788     return self.exception_context.__getattribute__(name)
789
790   def ReadMinidumpString(self, rva):
791     string = bytearray(MINIDUMP_STRING.Read(self.minidump, rva).buffer)
792     string = string.decode("utf16")
793     return string[0:len(string) - 1]
794
795   # Load FUNC records from a BreakPad symbol file
796   #
797   #    http://code.google.com/p/google-breakpad/wiki/SymbolFiles
798   #
799   def _LoadSymbolsFrom(self, symfile, baseaddr):
800     print "Loading symbols from %s" % (symfile)
801     funcs = []
802     with open(symfile) as f:
803       for line in f:
804         result = re.match(
805             r"^FUNC ([a-f0-9]+) ([a-f0-9]+) ([a-f0-9]+) (.*)$", line)
806         if result is not None:
807           start = int(result.group(1), 16)
808           size = int(result.group(2), 16)
809           name = result.group(4).rstrip()
810           bisect.insort_left(self.symbols,
811                              FuncSymbol(baseaddr + start, size, name))
812     print " ... done"
813
814   def TryLoadSymbolsFor(self, modulename, module):
815     try:
816       symfile = os.path.join(self.symdir,
817                              modulename.replace('.', '_') + ".pdb.sym")
818       if os.path.isfile(symfile):
819         self._LoadSymbolsFrom(symfile, module.base_of_image)
820         self.modules_with_symbols.append(module)
821     except Exception as e:
822       print "  ... failure (%s)" % (e)
823
824   # Returns true if address is covered by some module that has loaded symbols.
825   def _IsInModuleWithSymbols(self, addr):
826     for module in self.modules_with_symbols:
827       start = module.base_of_image
828       end = start + module.size_of_image
829       if (start <= addr) and (addr < end):
830         return True
831     return False
832
833   # Find symbol covering the given address and return its name in format
834   #     <symbol name>+<offset from the start>
835   def FindSymbol(self, addr):
836     if not self._IsInModuleWithSymbols(addr):
837       return None
838
839     i = bisect.bisect_left(self.symbols, addr)
840     symbol = None
841     if (0 < i) and self.symbols[i - 1].Covers(addr):
842       symbol = self.symbols[i - 1]
843     elif (i < len(self.symbols)) and self.symbols[i].Covers(addr):
844       symbol = self.symbols[i]
845     else:
846       return None
847     diff = addr - symbol.start
848     return "%s+0x%x" % (symbol.name, diff)
849
850
851 class Printer(object):
852   """Printer with indentation support."""
853
854   def __init__(self):
855     self.indent = 0
856
857   def Indent(self):
858     self.indent += 2
859
860   def Dedent(self):
861     self.indent -= 2
862
863   def Print(self, string):
864     print "%s%s" % (self._IndentString(), string)
865
866   def PrintLines(self, lines):
867     indent = self._IndentString()
868     print "\n".join("%s%s" % (indent, line) for line in lines)
869
870   def _IndentString(self):
871     return self.indent * " "
872
873
874 ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
875
876
877 def FormatDisasmLine(start, heap, line):
878   line_address = start + line[0]
879   stack_slot = heap.stack_map.get(line_address)
880   marker = "  "
881   if stack_slot:
882     marker = "=>"
883   code = AnnotateAddresses(heap, line[1])
884   return "%s%08x %08x: %s" % (marker, line_address, line[0], code)
885
886
887 def AnnotateAddresses(heap, line):
888   extra = []
889   for m in ADDRESS_RE.finditer(line):
890     maybe_address = int(m.group(0), 16)
891     object = heap.FindObject(maybe_address)
892     if not object: continue
893     extra.append(str(object))
894   if len(extra) == 0: return line
895   return "%s  ;; %s" % (line, ", ".join(extra))
896
897
898 class HeapObject(object):
899   def __init__(self, heap, map, address):
900     self.heap = heap
901     self.map = map
902     self.address = address
903
904   def Is(self, cls):
905     return isinstance(self, cls)
906
907   def Print(self, p):
908     p.Print(str(self))
909
910   def __str__(self):
911     return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address),
912                                    INSTANCE_TYPES[self.map.instance_type])
913
914   def ObjectField(self, offset):
915     field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
916     return self.heap.FindObjectOrSmi(field_value)
917
918   def SmiField(self, offset):
919     field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
920     assert (field_value & 1) == 0
921     return field_value / 2
922
923
924 class Map(HeapObject):
925   def Decode(self, offset, size, value):
926     return (value >> offset) & ((1 << size) - 1)
927
928   # Instance Sizes
929   def InstanceSizesOffset(self):
930     return self.heap.PointerSize()
931
932   def InstanceSizeOffset(self):
933     return self.InstanceSizesOffset()
934
935   def InObjectProperties(self):
936     return self.InstanceSizeOffset() + 1
937
938   def PreAllocatedPropertyFields(self):
939     return self.InObjectProperties() + 1
940
941   def VisitorId(self):
942     return self.PreAllocatedPropertyFields() + 1
943
944   # Instance Attributes
945   def InstanceAttributesOffset(self):
946     return self.InstanceSizesOffset() + self.heap.IntSize()
947
948   def InstanceTypeOffset(self):
949     return self.InstanceAttributesOffset()
950
951   def UnusedPropertyFieldsOffset(self):
952     return self.InstanceTypeOffset() + 1
953
954   def BitFieldOffset(self):
955     return self.UnusedPropertyFieldsOffset() + 1
956
957   def BitField2Offset(self):
958     return self.BitFieldOffset() + 1
959
960   # Other fields
961   def PrototypeOffset(self):
962     return self.InstanceAttributesOffset() + self.heap.IntSize()
963
964   def ConstructorOffset(self):
965     return self.PrototypeOffset() + self.heap.PointerSize()
966
967   def TransitionsOrBackPointerOffset(self):
968     return self.ConstructorOffset() + self.heap.PointerSize()
969
970   def DescriptorsOffset(self):
971     return self.TransitionsOrBackPointerOffset() + self.heap.PointerSize()
972
973   def CodeCacheOffset(self):
974     return self.DescriptorsOffset() + self.heap.PointerSize()
975
976   def DependentCodeOffset(self):
977     return self.CodeCacheOffset() + self.heap.PointerSize()
978
979   def BitField3Offset(self):
980     return self.DependentCodeOffset() + self.heap.PointerSize()
981
982   def ReadByte(self, offset):
983     return self.heap.reader.ReadU8(self.address + offset)
984
985   def Print(self, p):
986     p.Print("Map(%08x)" % (self.address))
987     p.Print("- size: %d, inobject: %d, preallocated: %d, visitor: %d" % (
988         self.ReadByte(self.InstanceSizeOffset()),
989         self.ReadByte(self.InObjectProperties()),
990         self.ReadByte(self.PreAllocatedPropertyFields()),
991         self.VisitorId()))
992
993     bitfield = self.ReadByte(self.BitFieldOffset())
994     bitfield2 = self.ReadByte(self.BitField2Offset())
995     p.Print("- %s, unused: %d, bf: %d, bf2: %d" % (
996         INSTANCE_TYPES[self.ReadByte(self.InstanceTypeOffset())],
997         self.ReadByte(self.UnusedPropertyFieldsOffset()),
998         bitfield, bitfield2))
999
1000     p.Print("- kind: %s" % (self.Decode(3, 5, bitfield2)))
1001
1002     bitfield3 = self.ObjectField(self.BitField3Offset())
1003     p.Print(
1004         "- EnumLength: %d NumberOfOwnDescriptors: %d OwnsDescriptors: %s" % (
1005             self.Decode(0, 11, bitfield3),
1006             self.Decode(11, 11, bitfield3),
1007             self.Decode(25, 1, bitfield3)))
1008     p.Print("- IsShared: %s" % (self.Decode(22, 1, bitfield3)))
1009     p.Print("- FunctionWithPrototype: %s" % (self.Decode(23, 1, bitfield3)))
1010     p.Print("- DictionaryMap: %s" % (self.Decode(24, 1, bitfield3)))
1011
1012     descriptors = self.ObjectField(self.DescriptorsOffset())
1013     if descriptors.__class__ == FixedArray:
1014       DescriptorArray(descriptors).Print(p)
1015     else:
1016       p.Print("Descriptors: %s" % (descriptors))
1017
1018     transitions = self.ObjectField(self.TransitionsOrBackPointerOffset())
1019     if transitions.__class__ == FixedArray:
1020       TransitionArray(transitions).Print(p)
1021     else:
1022       p.Print("TransitionsOrBackPointer: %s" % (transitions))
1023
1024   def __init__(self, heap, map, address):
1025     HeapObject.__init__(self, heap, map, address)
1026     self.instance_type = \
1027         heap.reader.ReadU8(self.address + self.InstanceTypeOffset())
1028
1029
1030 class String(HeapObject):
1031   def LengthOffset(self):
1032     # First word after the map is the hash, the second is the length.
1033     return self.heap.PointerSize() * 2
1034
1035   def __init__(self, heap, map, address):
1036     HeapObject.__init__(self, heap, map, address)
1037     self.length = self.SmiField(self.LengthOffset())
1038
1039   def GetChars(self):
1040     return "?string?"
1041
1042   def Print(self, p):
1043     p.Print(str(self))
1044
1045   def __str__(self):
1046     return "\"%s\"" % self.GetChars()
1047
1048
1049 class SeqString(String):
1050   def CharsOffset(self):
1051     return self.heap.PointerSize() * 3
1052
1053   def __init__(self, heap, map, address):
1054     String.__init__(self, heap, map, address)
1055     self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(),
1056                                        self.length)
1057
1058   def GetChars(self):
1059     return self.chars
1060
1061
1062 class ExternalString(String):
1063   # TODO(vegorov) fix ExternalString for X64 architecture
1064   RESOURCE_OFFSET = 12
1065
1066   WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
1067   WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
1068
1069   def __init__(self, heap, map, address):
1070     String.__init__(self, heap, map, address)
1071     reader = heap.reader
1072     self.resource = \
1073         reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
1074     self.chars = "?external string?"
1075     if not reader.IsValidAddress(self.resource): return
1076     string_impl_address = self.resource + \
1077         ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
1078     if not reader.IsValidAddress(string_impl_address): return
1079     string_impl = reader.ReadU32(string_impl_address)
1080     chars_ptr_address = string_impl + \
1081         ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
1082     if not reader.IsValidAddress(chars_ptr_address): return
1083     chars_ptr = reader.ReadU32(chars_ptr_address)
1084     if not reader.IsValidAddress(chars_ptr): return
1085     raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
1086     self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
1087
1088   def GetChars(self):
1089     return self.chars
1090
1091
1092 class ConsString(String):
1093   def LeftOffset(self):
1094     return self.heap.PointerSize() * 3
1095
1096   def RightOffset(self):
1097     return self.heap.PointerSize() * 4
1098
1099   def __init__(self, heap, map, address):
1100     String.__init__(self, heap, map, address)
1101     self.left = self.ObjectField(self.LeftOffset())
1102     self.right = self.ObjectField(self.RightOffset())
1103
1104   def GetChars(self):
1105     try:
1106       return self.left.GetChars() + self.right.GetChars()
1107     except:
1108       return "***CAUGHT EXCEPTION IN GROKDUMP***"
1109
1110
1111 class Oddball(HeapObject):
1112   # Should match declarations in objects.h
1113   KINDS = [
1114     "False",
1115     "True",
1116     "TheHole",
1117     "Null",
1118     "ArgumentMarker",
1119     "Undefined",
1120     "Other"
1121   ]
1122
1123   def ToStringOffset(self):
1124     return self.heap.PointerSize()
1125
1126   def ToNumberOffset(self):
1127     return self.ToStringOffset() + self.heap.PointerSize()
1128
1129   def KindOffset(self):
1130     return self.ToNumberOffset() + self.heap.PointerSize()
1131
1132   def __init__(self, heap, map, address):
1133     HeapObject.__init__(self, heap, map, address)
1134     self.to_string = self.ObjectField(self.ToStringOffset())
1135     self.kind = self.SmiField(self.KindOffset())
1136
1137   def Print(self, p):
1138     p.Print(str(self))
1139
1140   def __str__(self):
1141     if self.to_string:
1142       return "Oddball(%08x, <%s>)" % (self.address, str(self.to_string))
1143     else:
1144       kind = "???"
1145       if 0 <= self.kind < len(Oddball.KINDS):
1146         kind = Oddball.KINDS[self.kind]
1147       return "Oddball(%08x, kind=%s)" % (self.address, kind)
1148
1149
1150 class FixedArray(HeapObject):
1151   def LengthOffset(self):
1152     return self.heap.PointerSize()
1153
1154   def ElementsOffset(self):
1155     return self.heap.PointerSize() * 2
1156
1157   def MemberOffset(self, i):
1158     return self.ElementsOffset() + self.heap.PointerSize() * i
1159
1160   def Get(self, i):
1161     return self.ObjectField(self.MemberOffset(i))
1162
1163   def __init__(self, heap, map, address):
1164     HeapObject.__init__(self, heap, map, address)
1165     self.length = self.SmiField(self.LengthOffset())
1166
1167   def Print(self, p):
1168     p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1169     p.Indent()
1170     p.Print("length: %d" % self.length)
1171     base_offset = self.ElementsOffset()
1172     for i in xrange(self.length):
1173       offset = base_offset + 4 * i
1174       try:
1175         p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
1176       except TypeError:
1177         p.Dedent()
1178         p.Print("...")
1179         p.Print("}")
1180         return
1181     p.Dedent()
1182     p.Print("}")
1183
1184   def __str__(self):
1185     return "FixedArray(%08x, length=%d)" % (self.address, self.length)
1186
1187
1188 class DescriptorArray(object):
1189   def __init__(self, array):
1190     self.array = array
1191
1192   def Length(self):
1193     return self.array.Get(0)
1194
1195   def Decode(self, offset, size, value):
1196     return (value >> offset) & ((1 << size) - 1)
1197
1198   TYPES = [
1199       "normal",
1200       "field",
1201       "function",
1202       "callbacks"
1203   ]
1204
1205   def Type(self, value):
1206     return DescriptorArray.TYPES[self.Decode(0, 3, value)]
1207
1208   def Attributes(self, value):
1209     attributes = self.Decode(3, 3, value)
1210     result = []
1211     if (attributes & 0): result += ["ReadOnly"]
1212     if (attributes & 1): result += ["DontEnum"]
1213     if (attributes & 2): result += ["DontDelete"]
1214     return "[" + (",".join(result)) + "]"
1215
1216   def Deleted(self, value):
1217     return self.Decode(6, 1, value) == 1
1218
1219   def FieldIndex(self, value):
1220     return self.Decode(20, 11, value)
1221
1222   def Pointer(self, value):
1223     return self.Decode(6, 11, value)
1224
1225   def Details(self, di, value):
1226     return (
1227         di,
1228         self.Type(value),
1229         self.Attributes(value),
1230         self.FieldIndex(value),
1231         self.Pointer(value)
1232     )
1233
1234
1235   def Print(self, p):
1236     length = self.Length()
1237     array = self.array
1238
1239     p.Print("Descriptors(%08x, length=%d)" % (array.address, length))
1240     p.Print("[et] %s" % (array.Get(1)))
1241
1242     for di in xrange(length):
1243       i = 2 + di * 3
1244       p.Print("0x%x" % (array.address + array.MemberOffset(i)))
1245       p.Print("[%i] name:    %s" % (di, array.Get(i + 0)))
1246       p.Print("[%i] details: %s %s field-index %i pointer %i" % \
1247               self.Details(di, array.Get(i + 1)))
1248       p.Print("[%i] value:   %s" % (di, array.Get(i + 2)))
1249
1250     end = self.array.length // 3
1251     if length != end:
1252       p.Print("[%i-%i] slack descriptors" % (length, end))
1253
1254
1255 class TransitionArray(object):
1256   def __init__(self, array):
1257     self.array = array
1258
1259   def IsSimpleTransition(self):
1260     return self.array.length <= 2
1261
1262   def Length(self):
1263     # SimpleTransition cases
1264     if self.IsSimpleTransition():
1265       return self.array.length - 1
1266     return (self.array.length - 3) // 2
1267
1268   def Print(self, p):
1269     length = self.Length()
1270     array = self.array
1271
1272     p.Print("Transitions(%08x, length=%d)" % (array.address, length))
1273     p.Print("[backpointer] %s" % (array.Get(0)))
1274     if self.IsSimpleTransition():
1275       if length == 1:
1276         p.Print("[simple target] %s" % (array.Get(1)))
1277       return
1278
1279     elements = array.Get(1)
1280     if elements is not None:
1281       p.Print("[elements   ] %s" % (elements))
1282
1283     prototype = array.Get(2)
1284     if prototype is not None:
1285       p.Print("[prototype  ] %s" % (prototype))
1286
1287     for di in xrange(length):
1288       i = 3 + di * 2
1289       p.Print("[%i] symbol: %s" % (di, array.Get(i + 0)))
1290       p.Print("[%i] target: %s" % (di, array.Get(i + 1)))
1291
1292
1293 class JSFunction(HeapObject):
1294   def CodeEntryOffset(self):
1295     return 3 * self.heap.PointerSize()
1296
1297   def SharedOffset(self):
1298     return 5 * self.heap.PointerSize()
1299
1300   def __init__(self, heap, map, address):
1301     HeapObject.__init__(self, heap, map, address)
1302     code_entry = \
1303         heap.reader.ReadU32(self.address + self.CodeEntryOffset())
1304     self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1)
1305     self.shared = self.ObjectField(self.SharedOffset())
1306
1307   def Print(self, p):
1308     source = "\n".join("  %s" % line for line in self._GetSource().split("\n"))
1309     p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1310     p.Indent()
1311     p.Print("inferred name: %s" % self.shared.inferred_name)
1312     if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
1313       p.Print("script name: %s" % self.shared.script.name)
1314     p.Print("source:")
1315     p.PrintLines(self._GetSource().split("\n"))
1316     p.Print("code:")
1317     self.code.Print(p)
1318     if self.code != self.shared.code:
1319       p.Print("unoptimized code:")
1320       self.shared.code.Print(p)
1321     p.Dedent()
1322     p.Print("}")
1323
1324   def __str__(self):
1325     inferred_name = ""
1326     if self.shared.Is(SharedFunctionInfo):
1327       inferred_name = self.shared.inferred_name
1328     return "JSFunction(%s, %s)" % \
1329           (self.heap.reader.FormatIntPtr(self.address), inferred_name)
1330
1331   def _GetSource(self):
1332     source = "?source?"
1333     start = self.shared.start_position
1334     end = self.shared.end_position
1335     if not self.shared.script.Is(Script): return source
1336     script_source = self.shared.script.source
1337     if not script_source.Is(String): return source
1338     return script_source.GetChars()[start:end]
1339
1340
1341 class SharedFunctionInfo(HeapObject):
1342   def CodeOffset(self):
1343     return 2 * self.heap.PointerSize()
1344
1345   def ScriptOffset(self):
1346     return 7 * self.heap.PointerSize()
1347
1348   def InferredNameOffset(self):
1349     return 9 * self.heap.PointerSize()
1350
1351   def EndPositionOffset(self):
1352     return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
1353
1354   def StartPositionAndTypeOffset(self):
1355     return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
1356
1357   def __init__(self, heap, map, address):
1358     HeapObject.__init__(self, heap, map, address)
1359     self.code = self.ObjectField(self.CodeOffset())
1360     self.script = self.ObjectField(self.ScriptOffset())
1361     self.inferred_name = self.ObjectField(self.InferredNameOffset())
1362     if heap.PointerSize() == 8:
1363       start_position_and_type = \
1364           heap.reader.ReadU32(self.StartPositionAndTypeOffset())
1365       self.start_position = start_position_and_type >> 2
1366       pseudo_smi_end_position = \
1367           heap.reader.ReadU32(self.EndPositionOffset())
1368       self.end_position = pseudo_smi_end_position >> 2
1369     else:
1370       start_position_and_type = \
1371           self.SmiField(self.StartPositionAndTypeOffset())
1372       self.start_position = start_position_and_type >> 2
1373       self.end_position = \
1374           self.SmiField(self.EndPositionOffset())
1375
1376
1377 class Script(HeapObject):
1378   def SourceOffset(self):
1379     return self.heap.PointerSize()
1380
1381   def NameOffset(self):
1382     return self.SourceOffset() + self.heap.PointerSize()
1383
1384   def __init__(self, heap, map, address):
1385     HeapObject.__init__(self, heap, map, address)
1386     self.source = self.ObjectField(self.SourceOffset())
1387     self.name = self.ObjectField(self.NameOffset())
1388
1389
1390 class CodeCache(HeapObject):
1391   def DefaultCacheOffset(self):
1392     return self.heap.PointerSize()
1393
1394   def NormalTypeCacheOffset(self):
1395     return self.DefaultCacheOffset() + self.heap.PointerSize()
1396
1397   def __init__(self, heap, map, address):
1398     HeapObject.__init__(self, heap, map, address)
1399     self.default_cache = self.ObjectField(self.DefaultCacheOffset())
1400     self.normal_type_cache = self.ObjectField(self.NormalTypeCacheOffset())
1401
1402   def Print(self, p):
1403     p.Print("CodeCache(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1404     p.Indent()
1405     p.Print("default cache: %s" % self.default_cache)
1406     p.Print("normal type cache: %s" % self.normal_type_cache)
1407     p.Dedent()
1408     p.Print("}")
1409
1410
1411 class Code(HeapObject):
1412   CODE_ALIGNMENT_MASK = (1 << 5) - 1
1413
1414   def InstructionSizeOffset(self):
1415     return self.heap.PointerSize()
1416
1417   @staticmethod
1418   def HeaderSize(heap):
1419     return (heap.PointerSize() + heap.IntSize() + \
1420         4 * heap.PointerSize() + 3 * heap.IntSize() + \
1421         Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK
1422
1423   def __init__(self, heap, map, address):
1424     HeapObject.__init__(self, heap, map, address)
1425     self.entry = self.address + Code.HeaderSize(heap)
1426     self.instruction_size = \
1427         heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
1428
1429   def Print(self, p):
1430     lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
1431     p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1432     p.Indent()
1433     p.Print("instruction_size: %d" % self.instruction_size)
1434     p.PrintLines(self._FormatLine(line) for line in lines)
1435     p.Dedent()
1436     p.Print("}")
1437
1438   def _FormatLine(self, line):
1439     return FormatDisasmLine(self.entry, self.heap, line)
1440
1441
1442 class V8Heap(object):
1443   CLASS_MAP = {
1444     "SYMBOL_TYPE": SeqString,
1445     "ASCII_SYMBOL_TYPE": SeqString,
1446     "CONS_SYMBOL_TYPE": ConsString,
1447     "CONS_ASCII_SYMBOL_TYPE": ConsString,
1448     "EXTERNAL_SYMBOL_TYPE": ExternalString,
1449     "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
1450     "EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
1451     "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString,
1452     "SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
1453     "SHORT_EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
1454     "STRING_TYPE": SeqString,
1455     "ASCII_STRING_TYPE": SeqString,
1456     "CONS_STRING_TYPE": ConsString,
1457     "CONS_ASCII_STRING_TYPE": ConsString,
1458     "EXTERNAL_STRING_TYPE": ExternalString,
1459     "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE": ExternalString,
1460     "EXTERNAL_ASCII_STRING_TYPE": ExternalString,
1461     "MAP_TYPE": Map,
1462     "ODDBALL_TYPE": Oddball,
1463     "FIXED_ARRAY_TYPE": FixedArray,
1464     "JS_FUNCTION_TYPE": JSFunction,
1465     "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
1466     "SCRIPT_TYPE": Script,
1467     "CODE_CACHE_TYPE": CodeCache,
1468     "CODE_TYPE": Code,
1469   }
1470
1471   def __init__(self, reader, stack_map):
1472     self.reader = reader
1473     self.stack_map = stack_map
1474     self.objects = {}
1475
1476   def FindObjectOrSmi(self, tagged_address):
1477     if (tagged_address & 1) == 0: return tagged_address / 2
1478     return self.FindObject(tagged_address)
1479
1480   def FindObject(self, tagged_address):
1481     if tagged_address in self.objects:
1482       return self.objects[tagged_address]
1483     if (tagged_address & self.ObjectAlignmentMask()) != 1: return None
1484     address = tagged_address - 1
1485     if not self.reader.IsValidAddress(address): return None
1486     map_tagged_address = self.reader.ReadUIntPtr(address)
1487     if tagged_address == map_tagged_address:
1488       # Meta map?
1489       meta_map = Map(self, None, address)
1490       instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
1491       if instance_type_name != "MAP_TYPE": return None
1492       meta_map.map = meta_map
1493       object = meta_map
1494     else:
1495       map = self.FindMap(map_tagged_address)
1496       if map is None: return None
1497       instance_type_name = INSTANCE_TYPES.get(map.instance_type)
1498       if instance_type_name is None: return None
1499       cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
1500       object = cls(self, map, address)
1501     self.objects[tagged_address] = object
1502     return object
1503
1504   def FindMap(self, tagged_address):
1505     if (tagged_address & self.MapAlignmentMask()) != 1: return None
1506     address = tagged_address - 1
1507     if not self.reader.IsValidAddress(address): return None
1508     object = Map(self, None, address)
1509     return object
1510
1511   def IntSize(self):
1512     return 4
1513
1514   def PointerSize(self):
1515     return self.reader.PointerSize()
1516
1517   def ObjectAlignmentMask(self):
1518     return self.PointerSize() - 1
1519
1520   def MapAlignmentMask(self):
1521     if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64:
1522       return (1 << 4) - 1
1523     elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM:
1524       return (1 << 4) - 1
1525     elif self.reader.arch == MD_CPU_ARCHITECTURE_X86:
1526       return (1 << 5) - 1
1527
1528   def PageAlignmentMask(self):
1529     return (1 << 20) - 1
1530
1531
1532 class KnownObject(HeapObject):
1533   def __init__(self, heap, known_name):
1534     HeapObject.__init__(self, heap, None, None)
1535     self.known_name = known_name
1536
1537   def __str__(self):
1538     return "<%s>" % self.known_name
1539
1540
1541 class KnownMap(HeapObject):
1542   def __init__(self, heap, known_name, instance_type):
1543     HeapObject.__init__(self, heap, None, None)
1544     self.instance_type = instance_type
1545     self.known_name = known_name
1546
1547   def __str__(self):
1548     return "<%s>" % self.known_name
1549
1550
1551 class InspectionPadawan(object):
1552   """The padawan can improve annotations by sensing well-known objects."""
1553   def __init__(self, reader, heap):
1554     self.reader = reader
1555     self.heap = heap
1556     self.known_first_map_page = 0
1557     self.known_first_data_page = 0
1558     self.known_first_pointer_page = 0
1559
1560   def __getattr__(self, name):
1561     """An InspectionPadawan can be used instead of V8Heap, even though
1562        it does not inherit from V8Heap (aka. mixin)."""
1563     return getattr(self.heap, name)
1564
1565   def GetPageOffset(self, tagged_address):
1566     return tagged_address & self.heap.PageAlignmentMask()
1567
1568   def IsInKnownMapSpace(self, tagged_address):
1569     page_address = tagged_address & ~self.heap.PageAlignmentMask()
1570     return page_address == self.known_first_map_page
1571
1572   def IsInKnownOldSpace(self, tagged_address):
1573     page_address = tagged_address & ~self.heap.PageAlignmentMask()
1574     return page_address in [self.known_first_data_page,
1575                             self.known_first_pointer_page]
1576
1577   def ContainingKnownOldSpaceName(self, tagged_address):
1578     page_address = tagged_address & ~self.heap.PageAlignmentMask()
1579     if page_address == self.known_first_data_page: return "OLD_DATA_SPACE"
1580     if page_address == self.known_first_pointer_page: return "OLD_POINTER_SPACE"
1581     return None
1582
1583   def SenseObject(self, tagged_address):
1584     if self.IsInKnownOldSpace(tagged_address):
1585       offset = self.GetPageOffset(tagged_address)
1586       lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset)
1587       known_obj_name = KNOWN_OBJECTS.get(lookup_key)
1588       if known_obj_name:
1589         return KnownObject(self, known_obj_name)
1590     if self.IsInKnownMapSpace(tagged_address):
1591       known_map = self.SenseMap(tagged_address)
1592       if known_map:
1593         return known_map
1594     found_obj = self.heap.FindObject(tagged_address)
1595     if found_obj: return found_obj
1596     address = tagged_address - 1
1597     if self.reader.IsValidAddress(address):
1598       map_tagged_address = self.reader.ReadUIntPtr(address)
1599       map = self.SenseMap(map_tagged_address)
1600       if map is None: return None
1601       instance_type_name = INSTANCE_TYPES.get(map.instance_type)
1602       if instance_type_name is None: return None
1603       cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
1604       return cls(self, map, address)
1605     return None
1606
1607   def SenseMap(self, tagged_address):
1608     if self.IsInKnownMapSpace(tagged_address):
1609       offset = self.GetPageOffset(tagged_address)
1610       known_map_info = KNOWN_MAPS.get(offset)
1611       if known_map_info:
1612         known_map_type, known_map_name = known_map_info
1613         return KnownMap(self, known_map_name, known_map_type)
1614     found_map = self.heap.FindMap(tagged_address)
1615     if found_map: return found_map
1616     return None
1617
1618   def FindObjectOrSmi(self, tagged_address):
1619     """When used as a mixin in place of V8Heap."""
1620     found_obj = self.SenseObject(tagged_address)
1621     if found_obj: return found_obj
1622     if (tagged_address & 1) == 0:
1623       return "Smi(%d)" % (tagged_address / 2)
1624     else:
1625       return "Unknown(%s)" % self.reader.FormatIntPtr(tagged_address)
1626
1627   def FindObject(self, tagged_address):
1628     """When used as a mixin in place of V8Heap."""
1629     raise NotImplementedError
1630
1631   def FindMap(self, tagged_address):
1632     """When used as a mixin in place of V8Heap."""
1633     raise NotImplementedError
1634
1635   def PrintKnowledge(self):
1636     print "  known_first_map_page = %s\n"\
1637           "  known_first_data_page = %s\n"\
1638           "  known_first_pointer_page = %s" % (
1639           self.reader.FormatIntPtr(self.known_first_map_page),
1640           self.reader.FormatIntPtr(self.known_first_data_page),
1641           self.reader.FormatIntPtr(self.known_first_pointer_page))
1642
1643
1644 class InspectionShell(cmd.Cmd):
1645   def __init__(self, reader, heap):
1646     cmd.Cmd.__init__(self)
1647     self.reader = reader
1648     self.heap = heap
1649     self.padawan = InspectionPadawan(reader, heap)
1650     self.prompt = "(grok) "
1651
1652   def do_da(self, address):
1653     """
1654      Print ASCII string starting at specified address.
1655     """
1656     address = int(address, 16)
1657     string = ""
1658     while self.reader.IsValidAddress(address):
1659       code = self.reader.ReadU8(address)
1660       if code < 128:
1661         string += chr(code)
1662       else:
1663         break
1664       address += 1
1665     if string == "":
1666       print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address)
1667     else:
1668       print "%s\n" % string
1669
1670   def do_dd(self, address):
1671     """
1672      Interpret memory at the given address (if available) as a sequence
1673      of words. Automatic alignment is not performed.
1674     """
1675     start = int(address, 16)
1676     if (start & self.heap.ObjectAlignmentMask()) != 0:
1677       print "Warning: Dumping un-aligned memory, is this what you had in mind?"
1678     for slot in xrange(start,
1679                        start + self.reader.PointerSize() * 10,
1680                        self.reader.PointerSize()):
1681       if not self.reader.IsValidAddress(slot):
1682         print "Address is not contained within the minidump!"
1683         return
1684       maybe_address = self.reader.ReadUIntPtr(slot)
1685       heap_object = self.padawan.SenseObject(maybe_address)
1686       print "%s: %s %s" % (self.reader.FormatIntPtr(slot),
1687                            self.reader.FormatIntPtr(maybe_address),
1688                            heap_object or '')
1689
1690   def do_do(self, address):
1691     """
1692      Interpret memory at the given address as a V8 object. Automatic
1693      alignment makes sure that you can pass tagged as well as un-tagged
1694      addresses.
1695     """
1696     address = int(address, 16)
1697     if (address & self.heap.ObjectAlignmentMask()) == 0:
1698       address = address + 1
1699     elif (address & self.heap.ObjectAlignmentMask()) != 1:
1700       print "Address doesn't look like a valid pointer!"
1701       return
1702     heap_object = self.padawan.SenseObject(address)
1703     if heap_object:
1704       heap_object.Print(Printer())
1705     else:
1706       print "Address cannot be interpreted as object!"
1707
1708   def do_do_desc(self, address):
1709     """
1710       Print a descriptor array in a readable format.
1711     """
1712     start = int(address, 16)
1713     if ((start & 1) == 1): start = start - 1
1714     DescriptorArray(FixedArray(self.heap, None, start)).Print(Printer())
1715
1716   def do_do_map(self, address):
1717     """
1718       Print a descriptor array in a readable format.
1719     """
1720     start = int(address, 16)
1721     if ((start & 1) == 1): start = start - 1
1722     Map(self.heap, None, start).Print(Printer())
1723
1724   def do_do_trans(self, address):
1725     """
1726       Print a transition array in a readable format.
1727     """
1728     start = int(address, 16)
1729     if ((start & 1) == 1): start = start - 1
1730     TransitionArray(FixedArray(self.heap, None, start)).Print(Printer())
1731
1732   def do_dp(self, address):
1733     """
1734      Interpret memory at the given address as being on a V8 heap page
1735      and print information about the page header (if available).
1736     """
1737     address = int(address, 16)
1738     page_address = address & ~self.heap.PageAlignmentMask()
1739     if self.reader.IsValidAddress(page_address):
1740       raise NotImplementedError
1741     else:
1742       print "Page header is not available!"
1743
1744   def do_k(self, arguments):
1745     """
1746      Teach V8 heap layout information to the inspector. This increases
1747      the amount of annotations the inspector can produce while dumping
1748      data. The first page of each heap space is of particular interest
1749      because it contains known objects that do not move.
1750     """
1751     self.padawan.PrintKnowledge()
1752
1753   def do_kd(self, address):
1754     """
1755      Teach V8 heap layout information to the inspector. Set the first
1756      data-space page by passing any pointer into that page.
1757     """
1758     address = int(address, 16)
1759     page_address = address & ~self.heap.PageAlignmentMask()
1760     self.padawan.known_first_data_page = page_address
1761
1762   def do_km(self, address):
1763     """
1764      Teach V8 heap layout information to the inspector. Set the first
1765      map-space page by passing any pointer into that page.
1766     """
1767     address = int(address, 16)
1768     page_address = address & ~self.heap.PageAlignmentMask()
1769     self.padawan.known_first_map_page = page_address
1770
1771   def do_kp(self, address):
1772     """
1773      Teach V8 heap layout information to the inspector. Set the first
1774      pointer-space page by passing any pointer into that page.
1775     """
1776     address = int(address, 16)
1777     page_address = address & ~self.heap.PageAlignmentMask()
1778     self.padawan.known_first_pointer_page = page_address
1779
1780   def do_list(self, smth):
1781     """
1782      List all available memory regions.
1783     """
1784     def print_region(reader, start, size, location):
1785       print "  %s - %s (%d bytes)" % (reader.FormatIntPtr(start),
1786                                       reader.FormatIntPtr(start + size),
1787                                       size)
1788     print "Available memory regions:"
1789     self.reader.ForEachMemoryRegion(print_region)
1790
1791   def do_lm(self, arg):
1792     """
1793      List details for all loaded modules in the minidump. An argument can
1794      be passed to limit the output to only those modules that contain the
1795      argument as a substring (case insensitive match).
1796     """
1797     for module in self.reader.module_list.modules:
1798       if arg:
1799         name = GetModuleName(self.reader, module).lower()
1800         if name.find(arg.lower()) >= 0:
1801           PrintModuleDetails(self.reader, module)
1802       else:
1803         PrintModuleDetails(self.reader, module)
1804     print
1805
1806   def do_s(self, word):
1807     """
1808      Search for a given word in available memory regions. The given word
1809      is expanded to full pointer size and searched at aligned as well as
1810      un-aligned memory locations. Use 'sa' to search aligned locations
1811      only.
1812     """
1813     try:
1814       word = int(word, 0)
1815     except ValueError:
1816       print "Malformed word, prefix with '0x' to use hexadecimal format."
1817       return
1818     print "Searching for word %d/0x%s:" % (word, self.reader.FormatIntPtr(word))
1819     self.reader.FindWord(word)
1820
1821   def do_sh(self, none):
1822     """
1823      Search for the V8 Heap object in all available memory regions. You
1824      might get lucky and find this rare treasure full of invaluable
1825      information.
1826     """
1827     raise NotImplementedError
1828
1829   def do_u(self, args):
1830     """
1831      Unassemble memory in the region [address, address + size). If the
1832      size is not specified, a default value of 32 bytes is used.
1833      Synopsis: u 0x<address> 0x<size>
1834     """
1835     args = args.split(' ')
1836     start = int(args[0], 16)
1837     size = int(args[1], 16) if len(args) > 1 else 0x20
1838     if not self.reader.IsValidAddress(start):
1839       print "Address is not contained within the minidump!"
1840       return
1841     lines = self.reader.GetDisasmLines(start, size)
1842     for line in lines:
1843       print FormatDisasmLine(start, self.heap, line)
1844     print
1845
1846   def do_EOF(self, none):
1847     raise KeyboardInterrupt
1848
1849 EIP_PROXIMITY = 64
1850
1851 CONTEXT_FOR_ARCH = {
1852     MD_CPU_ARCHITECTURE_AMD64:
1853       ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip',
1854        'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'],
1855     MD_CPU_ARCHITECTURE_ARM:
1856       ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
1857        'r10', 'r11', 'r12', 'sp', 'lr', 'pc'],
1858     MD_CPU_ARCHITECTURE_X86:
1859       ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
1860 }
1861
1862 KNOWN_MODULES = {'chrome.exe', 'chrome.dll'}
1863
1864 def GetVersionString(ms, ls):
1865   return "%d.%d.%d.%d" % (ms >> 16, ms & 0xffff, ls >> 16, ls & 0xffff)
1866
1867
1868 def GetModuleName(reader, module):
1869   name = reader.ReadMinidumpString(module.module_name_rva)
1870   # simplify for path manipulation
1871   name = name.encode('utf-8')
1872   return str(os.path.basename(str(name).replace("\\", "/")))
1873
1874
1875 def PrintModuleDetails(reader, module):
1876   print "%s" % GetModuleName(reader, module)
1877   file_version = GetVersionString(module.version_info.dwFileVersionMS,
1878                                   module.version_info.dwFileVersionLS)
1879   product_version = GetVersionString(module.version_info.dwProductVersionMS,
1880                                      module.version_info.dwProductVersionLS)
1881   print "  base: %s" % reader.FormatIntPtr(module.base_of_image)
1882   print "  end: %s" % reader.FormatIntPtr(module.base_of_image +
1883                                           module.size_of_image)
1884   print "  file version: %s" % file_version
1885   print "  product version: %s" % product_version
1886   time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
1887   print "  timestamp: %s" % time_date_stamp
1888
1889
1890 def AnalyzeMinidump(options, minidump_name):
1891   reader = MinidumpReader(options, minidump_name)
1892   heap = None
1893   DebugPrint("========================================")
1894   if reader.exception is None:
1895     print "Minidump has no exception info"
1896   else:
1897     print "Exception info:"
1898     exception_thread = reader.thread_map[reader.exception.thread_id]
1899     print "  thread id: %d" % exception_thread.id
1900     print "  code: %08X" % reader.exception.exception.code
1901     print "  context:"
1902     for r in CONTEXT_FOR_ARCH[reader.arch]:
1903       print "    %s: %s" % (r, reader.FormatIntPtr(reader.Register(r)))
1904     # TODO(vitalyr): decode eflags.
1905     if reader.arch == MD_CPU_ARCHITECTURE_ARM:
1906       print "    cpsr: %s" % bin(reader.exception_context.cpsr)[2:]
1907     else:
1908       print "    eflags: %s" % bin(reader.exception_context.eflags)[2:]
1909
1910     print
1911     print "  modules:"
1912     for module in reader.module_list.modules:
1913       name = GetModuleName(reader, module)
1914       if name in KNOWN_MODULES:
1915         print "    %s at %08X" % (name, module.base_of_image)
1916         reader.TryLoadSymbolsFor(name, module)
1917     print
1918
1919     stack_top = reader.ExceptionSP()
1920     stack_bottom = exception_thread.stack.start + \
1921         exception_thread.stack.memory.data_size
1922     stack_map = {reader.ExceptionIP(): -1}
1923     for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
1924       maybe_address = reader.ReadUIntPtr(slot)
1925       if not maybe_address in stack_map:
1926         stack_map[maybe_address] = slot
1927     heap = V8Heap(reader, stack_map)
1928
1929     print "Disassembly around exception.eip:"
1930     eip_symbol = reader.FindSymbol(reader.ExceptionIP())
1931     if eip_symbol is not None:
1932       print eip_symbol
1933     disasm_start = reader.ExceptionIP() - EIP_PROXIMITY
1934     disasm_bytes = 2 * EIP_PROXIMITY
1935     if (options.full):
1936       full_range = reader.FindRegion(reader.ExceptionIP())
1937       if full_range is not None:
1938         disasm_start = full_range[0]
1939         disasm_bytes = full_range[1]
1940
1941     lines = reader.GetDisasmLines(disasm_start, disasm_bytes)
1942
1943     for line in lines:
1944       print FormatDisasmLine(disasm_start, heap, line)
1945     print
1946
1947   if heap is None:
1948     heap = V8Heap(reader, None)
1949
1950   if options.full:
1951     FullDump(reader, heap)
1952
1953   if options.command:
1954     InspectionShell(reader, heap).onecmd(options.command)
1955
1956   if options.shell:
1957     try:
1958       InspectionShell(reader, heap).cmdloop("type help to get help")
1959     except KeyboardInterrupt:
1960       print "Kthxbye."
1961   elif not options.command:
1962     if reader.exception is not None:
1963       frame_pointer = reader.ExceptionFP()
1964       print "Annotated stack (from exception.esp to bottom):"
1965       for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
1966         maybe_address = reader.ReadUIntPtr(slot)
1967         heap_object = heap.FindObject(maybe_address)
1968         maybe_symbol = reader.FindSymbol(maybe_address)
1969         if slot == frame_pointer:
1970           maybe_symbol = "<---- frame pointer"
1971           frame_pointer = maybe_address
1972         print "%s: %s %s" % (reader.FormatIntPtr(slot),
1973                              reader.FormatIntPtr(maybe_address),
1974                              maybe_symbol or "")
1975         if heap_object:
1976           heap_object.Print(Printer())
1977           print
1978
1979   reader.Dispose()
1980
1981
1982 if __name__ == "__main__":
1983   parser = optparse.OptionParser(USAGE)
1984   parser.add_option("-s", "--shell", dest="shell", action="store_true",
1985                     help="start an interactive inspector shell")
1986   parser.add_option("-c", "--command", dest="command", default="",
1987                     help="run an interactive inspector shell command and exit")
1988   parser.add_option("-f", "--full", dest="full", action="store_true",
1989                     help="dump all information contained in the minidump")
1990   parser.add_option("--symdir", dest="symdir", default=".",
1991                     help="directory containing *.pdb.sym file with symbols")
1992   parser.add_option("--objdump",
1993                     default="/usr/bin/objdump",
1994                     help="objdump tool to use [default: %default]")
1995   options, args = parser.parse_args()
1996   if os.path.exists(options.objdump):
1997     disasm.OBJDUMP_BIN = options.objdump
1998     OBJDUMP_BIN = options.objdump
1999   else:
2000     print "Cannot find %s, falling back to default objdump" % options.objdump
2001   if len(args) != 1:
2002     parser.print_help()
2003     sys.exit(1)
2004   AnalyzeMinidump(options, args[0])