• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
30import BaseHTTPServer
31import bisect
32import cgi
33import cmd
34import codecs
35import ctypes
36import datetime
37import disasm
38import inspect
39import mmap
40import optparse
41import os
42import re
43import StringIO
44import sys
45import types
46import urllib
47import urlparse
48import v8heapconst
49import webbrowser
50
51PORT_NUMBER = 8081
52
53
54USAGE="""usage: %prog [OPTIONS] [DUMP-FILE]
55
56Minidump analyzer.
57
58Shows the processor state at the point of exception including the
59stack of the active thread and the referenced objects in the V8
60heap. Code objects are disassembled and the addresses linked from the
61stack (e.g. pushed return addresses) are marked with "=>".
62
63Examples:
64  $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp"""
65
66
67DEBUG=False
68
69
70def DebugPrint(s):
71  if not DEBUG: return
72  print s
73
74
75class Descriptor(object):
76  """Descriptor of a structure in a memory."""
77
78  def __init__(self, fields):
79    self.fields = fields
80    self.is_flexible = False
81    for _, type_or_func in fields:
82      if isinstance(type_or_func, types.FunctionType):
83        self.is_flexible = True
84        break
85    if not self.is_flexible:
86      self.ctype = Descriptor._GetCtype(fields)
87      self.size = ctypes.sizeof(self.ctype)
88
89  def Read(self, memory, offset):
90    if self.is_flexible:
91      fields_copy = self.fields[:]
92      last = 0
93      for name, type_or_func in fields_copy:
94        if isinstance(type_or_func, types.FunctionType):
95          partial_ctype = Descriptor._GetCtype(fields_copy[:last])
96          partial_object = partial_ctype.from_buffer(memory, offset)
97          type = type_or_func(partial_object)
98          if type is not None:
99            fields_copy[last] = (name, type)
100            last += 1
101        else:
102          last += 1
103      complete_ctype = Descriptor._GetCtype(fields_copy[:last])
104    else:
105      complete_ctype = self.ctype
106    return complete_ctype.from_buffer(memory, offset)
107
108  @staticmethod
109  def _GetCtype(fields):
110    class Raw(ctypes.Structure):
111      _fields_ = fields
112      _pack_ = 1
113
114      def __str__(self):
115        return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field))
116                               for field, _ in Raw._fields_) + "}"
117    return Raw
118
119
120def FullDump(reader, heap):
121  """Dump all available memory regions."""
122  def dump_region(reader, start, size, location):
123    print
124    while start & 3 != 0:
125      start += 1
126      size -= 1
127      location += 1
128    is_executable = reader.IsProbableExecutableRegion(location, size)
129    is_ascii = reader.IsProbableASCIIRegion(location, size)
130
131    if is_executable is not False:
132      lines = reader.GetDisasmLines(start, size)
133      for line in lines:
134        print FormatDisasmLine(start, heap, line)
135      print
136
137    if is_ascii is not False:
138      # Output in the same format as the Unix hd command
139      addr = start
140      for i in xrange(0, size, 16):
141        slot = i + location
142        hex_line = ""
143        asc_line = ""
144        for i in xrange(16):
145          if slot + i < location + size:
146            byte = ctypes.c_uint8.from_buffer(reader.minidump, slot + i).value
147            if byte >= 0x20 and byte < 0x7f:
148              asc_line += chr(byte)
149            else:
150              asc_line += "."
151            hex_line += " %02x" % (byte)
152          else:
153            hex_line += "   "
154          if i == 7:
155            hex_line += " "
156        print "%s  %s |%s|" % (reader.FormatIntPtr(addr),
157                               hex_line,
158                               asc_line)
159        addr += 16
160
161    if is_executable is not True and is_ascii is not True:
162      print "%s - %s" % (reader.FormatIntPtr(start),
163                         reader.FormatIntPtr(start + size))
164      print start + size + 1;
165      for i in xrange(0, size, reader.PointerSize()):
166        slot = start + i
167        maybe_address = reader.ReadUIntPtr(slot)
168        heap_object = heap.FindObject(maybe_address)
169        print "%s: %s" % (reader.FormatIntPtr(slot),
170                          reader.FormatIntPtr(maybe_address))
171        if heap_object:
172          heap_object.Print(Printer())
173          print
174
175  reader.ForEachMemoryRegion(dump_region)
176
177# Heap constants generated by 'make grokdump' in v8heapconst module.
178INSTANCE_TYPES = v8heapconst.INSTANCE_TYPES
179KNOWN_MAPS = v8heapconst.KNOWN_MAPS
180KNOWN_OBJECTS = v8heapconst.KNOWN_OBJECTS
181FRAME_MARKERS = v8heapconst.FRAME_MARKERS
182
183# Markers pushed on the stack by PushStackTraceAndDie
184MAGIC_MARKER_PAIRS = (
185    (0xbbbbbbbb, 0xbbbbbbbb),
186    (0xfefefefe, 0xfefefeff),
187)
188# See StackTraceFailureMessage in isolate.h
189STACK_TRACE_MARKER = 0xdecade30
190# See FailureMessage in logging.cc
191ERROR_MESSAGE_MARKER = 0xdecade10
192
193# Set of structures and constants that describe the layout of minidump
194# files. Based on MSDN and Google Breakpad.
195
196MINIDUMP_HEADER = Descriptor([
197  ("signature", ctypes.c_uint32),
198  ("version", ctypes.c_uint32),
199  ("stream_count", ctypes.c_uint32),
200  ("stream_directories_rva", ctypes.c_uint32),
201  ("checksum", ctypes.c_uint32),
202  ("time_date_stampt", ctypes.c_uint32),
203  ("flags", ctypes.c_uint64)
204])
205
206MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
207  ("data_size", ctypes.c_uint32),
208  ("rva", ctypes.c_uint32)
209])
210
211MINIDUMP_STRING = Descriptor([
212  ("length", ctypes.c_uint32),
213  ("buffer", lambda t: ctypes.c_uint8 * (t.length + 2))
214])
215
216MINIDUMP_DIRECTORY = Descriptor([
217  ("stream_type", ctypes.c_uint32),
218  ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
219])
220
221MD_EXCEPTION_MAXIMUM_PARAMETERS = 15
222
223MINIDUMP_EXCEPTION = Descriptor([
224  ("code", ctypes.c_uint32),
225  ("flags", ctypes.c_uint32),
226  ("record", ctypes.c_uint64),
227  ("address", ctypes.c_uint64),
228  ("parameter_count", ctypes.c_uint32),
229  ("unused_alignment", ctypes.c_uint32),
230  ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS)
231])
232
233MINIDUMP_EXCEPTION_STREAM = Descriptor([
234  ("thread_id", ctypes.c_uint32),
235  ("unused_alignment", ctypes.c_uint32),
236  ("exception", MINIDUMP_EXCEPTION.ctype),
237  ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
238])
239
240# Stream types.
241MD_UNUSED_STREAM = 0
242MD_RESERVED_STREAM_0 = 1
243MD_RESERVED_STREAM_1 = 2
244MD_THREAD_LIST_STREAM = 3
245MD_MODULE_LIST_STREAM = 4
246MD_MEMORY_LIST_STREAM = 5
247MD_EXCEPTION_STREAM = 6
248MD_SYSTEM_INFO_STREAM = 7
249MD_THREAD_EX_LIST_STREAM = 8
250MD_MEMORY_64_LIST_STREAM = 9
251MD_COMMENT_STREAM_A = 10
252MD_COMMENT_STREAM_W = 11
253MD_HANDLE_DATA_STREAM = 12
254MD_FUNCTION_TABLE_STREAM = 13
255MD_UNLOADED_MODULE_LIST_STREAM = 14
256MD_MISC_INFO_STREAM = 15
257MD_MEMORY_INFO_LIST_STREAM = 16
258MD_THREAD_INFO_LIST_STREAM = 17
259MD_HANDLE_OPERATION_LIST_STREAM = 18
260
261MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80
262
263MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([
264  ("control_word", ctypes.c_uint32),
265  ("status_word", ctypes.c_uint32),
266  ("tag_word", ctypes.c_uint32),
267  ("error_offset", ctypes.c_uint32),
268  ("error_selector", ctypes.c_uint32),
269  ("data_offset", ctypes.c_uint32),
270  ("data_selector", ctypes.c_uint32),
271  ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE),
272  ("cr0_npx_state", ctypes.c_uint32)
273])
274
275MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512
276
277# Context flags.
278MD_CONTEXT_X86 = 0x00010000
279MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001)
280MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002)
281MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004)
282MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008)
283MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010)
284MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020)
285
286def EnableOnFlag(type, flag):
287  return lambda o: [None, type][int((o.context_flags & flag) != 0)]
288
289MINIDUMP_CONTEXT_X86 = Descriptor([
290  ("context_flags", ctypes.c_uint32),
291  # MD_CONTEXT_X86_DEBUG_REGISTERS.
292  ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
293  ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
294  ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
295  ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
296  ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
297  ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
298  # MD_CONTEXT_X86_FLOATING_POINT.
299  ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype,
300                              MD_CONTEXT_X86_FLOATING_POINT)),
301  # MD_CONTEXT_X86_SEGMENTS.
302  ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
303  ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
304  ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
305  ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
306  # MD_CONTEXT_X86_INTEGER.
307  ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
308  ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
309  ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
310  ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
311  ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
312  ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
313  # MD_CONTEXT_X86_CONTROL.
314  ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
315  ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
316  ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
317  ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
318  ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
319  ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
320  # MD_CONTEXT_X86_EXTENDED_REGISTERS.
321  ("extended_registers",
322   EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE,
323                MD_CONTEXT_X86_EXTENDED_REGISTERS))
324])
325
326MD_CONTEXT_ARM = 0x40000000
327MD_CONTEXT_ARM_INTEGER = (MD_CONTEXT_ARM | 0x00000002)
328MD_CONTEXT_ARM_FLOATING_POINT = (MD_CONTEXT_ARM | 0x00000004)
329MD_FLOATINGSAVEAREA_ARM_FPR_COUNT = 32
330MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT = 8
331
332MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([
333  ("fpscr", ctypes.c_uint64),
334  ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPR_COUNT),
335  ("extra", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT)
336])
337
338MINIDUMP_CONTEXT_ARM = Descriptor([
339  ("context_flags", ctypes.c_uint32),
340  # MD_CONTEXT_ARM_INTEGER.
341  ("r0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
342  ("r1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
343  ("r2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
344  ("r3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
345  ("r4", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
346  ("r5", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
347  ("r6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
348  ("r7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
349  ("r8", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
350  ("r9", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
351  ("r10", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
352  ("r11", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
353  ("r12", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
354  ("sp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
355  ("lr", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
356  ("pc", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
357  ("cpsr", ctypes.c_uint32),
358  ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype,
359                              MD_CONTEXT_ARM_FLOATING_POINT))
360])
361
362
363MD_CONTEXT_ARM64 =  0x80000000
364MD_CONTEXT_ARM64_INTEGER = (MD_CONTEXT_ARM64 | 0x00000002)
365MD_CONTEXT_ARM64_FLOATING_POINT = (MD_CONTEXT_ARM64 | 0x00000004)
366MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT = 64
367
368MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([
369  ("fpscr", ctypes.c_uint64),
370  ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT),
371])
372
373MINIDUMP_CONTEXT_ARM64 = Descriptor([
374  ("context_flags", ctypes.c_uint64),
375  # MD_CONTEXT_ARM64_INTEGER.
376  ("r0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
377  ("r1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
378  ("r2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
379  ("r3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
380  ("r4", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
381  ("r5", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
382  ("r6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
383  ("r7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
384  ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
385  ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
386  ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
387  ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
388  ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
389  ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
390  ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
391  ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
392  ("r16", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
393  ("r17", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
394  ("r18", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
395  ("r19", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
396  ("r20", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
397  ("r21", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
398  ("r22", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
399  ("r23", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
400  ("r24", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
401  ("r25", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
402  ("r26", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
403  ("r27", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
404  ("r28", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
405  ("fp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
406  ("lr", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
407  ("sp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
408  ("pc", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
409  ("cpsr", ctypes.c_uint32),
410  ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype,
411                              MD_CONTEXT_ARM64_FLOATING_POINT))
412])
413
414
415MD_CONTEXT_AMD64 = 0x00100000
416MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001)
417MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002)
418MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004)
419MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008)
420MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010)
421
422MINIDUMP_CONTEXT_AMD64 = Descriptor([
423  ("p1_home", ctypes.c_uint64),
424  ("p2_home", ctypes.c_uint64),
425  ("p3_home", ctypes.c_uint64),
426  ("p4_home", ctypes.c_uint64),
427  ("p5_home", ctypes.c_uint64),
428  ("p6_home", ctypes.c_uint64),
429  ("context_flags", ctypes.c_uint32),
430  ("mx_csr", ctypes.c_uint32),
431  # MD_CONTEXT_AMD64_CONTROL.
432  ("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
433  # MD_CONTEXT_AMD64_SEGMENTS
434  ("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
435  ("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
436  ("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
437  ("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
438  # MD_CONTEXT_AMD64_CONTROL.
439  ("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
440  ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)),
441  # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
442  ("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
443  ("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
444  ("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
445  ("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
446  ("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
447  ("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
448  # MD_CONTEXT_AMD64_INTEGER.
449  ("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
450  ("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
451  ("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
452  ("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
453  # MD_CONTEXT_AMD64_CONTROL.
454  ("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
455  # MD_CONTEXT_AMD64_INTEGER.
456  ("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
457  ("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
458  ("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
459  ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
460  ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
461  ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
462  ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
463  ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
464  ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
465  ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
466  ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
467  # MD_CONTEXT_AMD64_CONTROL.
468  ("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
469  # MD_CONTEXT_AMD64_FLOATING_POINT
470  ("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
471                                 MD_CONTEXT_AMD64_FLOATING_POINT)),
472  ("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
473                                    MD_CONTEXT_AMD64_FLOATING_POINT)),
474  ("vector_control", EnableOnFlag(ctypes.c_uint64,
475                                  MD_CONTEXT_AMD64_FLOATING_POINT)),
476  # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
477  ("debug_control", EnableOnFlag(ctypes.c_uint64,
478                                 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
479  ("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64,
480                                      MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
481  ("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64,
482                                        MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
483  ("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64,
484                                         MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
485  ("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64,
486                                           MD_CONTEXT_AMD64_DEBUG_REGISTERS))
487])
488
489MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
490  ("start", ctypes.c_uint64),
491  ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
492])
493
494MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
495  ("start", ctypes.c_uint64),
496  ("size", ctypes.c_uint64)
497])
498
499MINIDUMP_MEMORY_LIST = Descriptor([
500  ("range_count", ctypes.c_uint32),
501  ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
502])
503
504MINIDUMP_MEMORY_LIST_Mac = Descriptor([
505  ("range_count", ctypes.c_uint32),
506  ("junk", ctypes.c_uint32),
507  ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
508])
509
510MINIDUMP_MEMORY_LIST64 = Descriptor([
511  ("range_count", ctypes.c_uint64),
512  ("base_rva", ctypes.c_uint64),
513  ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count)
514])
515
516MINIDUMP_THREAD = Descriptor([
517  ("id", ctypes.c_uint32),
518  ("suspend_count", ctypes.c_uint32),
519  ("priority_class", ctypes.c_uint32),
520  ("priority", ctypes.c_uint32),
521  ("ted", ctypes.c_uint64),
522  ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype),
523  ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
524])
525
526MINIDUMP_THREAD_LIST = Descriptor([
527  ("thread_count", ctypes.c_uint32),
528  ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
529])
530
531MINIDUMP_THREAD_LIST_Mac = Descriptor([
532  ("thread_count", ctypes.c_uint32),
533  ("junk", ctypes.c_uint32),
534  ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
535])
536
537MINIDUMP_VS_FIXEDFILEINFO = Descriptor([
538  ("dwSignature", ctypes.c_uint32),
539  ("dwStrucVersion", ctypes.c_uint32),
540  ("dwFileVersionMS", ctypes.c_uint32),
541  ("dwFileVersionLS", ctypes.c_uint32),
542  ("dwProductVersionMS", ctypes.c_uint32),
543  ("dwProductVersionLS", ctypes.c_uint32),
544  ("dwFileFlagsMask", ctypes.c_uint32),
545  ("dwFileFlags", ctypes.c_uint32),
546  ("dwFileOS", ctypes.c_uint32),
547  ("dwFileType", ctypes.c_uint32),
548  ("dwFileSubtype", ctypes.c_uint32),
549  ("dwFileDateMS", ctypes.c_uint32),
550  ("dwFileDateLS", ctypes.c_uint32)
551])
552
553MINIDUMP_RAW_MODULE = Descriptor([
554  ("base_of_image", ctypes.c_uint64),
555  ("size_of_image", ctypes.c_uint32),
556  ("checksum", ctypes.c_uint32),
557  ("time_date_stamp", ctypes.c_uint32),
558  ("module_name_rva", ctypes.c_uint32),
559  ("version_info", MINIDUMP_VS_FIXEDFILEINFO.ctype),
560  ("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
561  ("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
562  ("reserved0", ctypes.c_uint32 * 2),
563  ("reserved1", ctypes.c_uint32 * 2)
564])
565
566MINIDUMP_MODULE_LIST = Descriptor([
567  ("number_of_modules", ctypes.c_uint32),
568  ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules)
569])
570
571MINIDUMP_MODULE_LIST_Mac = Descriptor([
572  ("number_of_modules", ctypes.c_uint32),
573  ("junk", ctypes.c_uint32),
574  ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules)
575])
576
577MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
578  ("processor_architecture", ctypes.c_uint16)
579])
580
581MD_CPU_ARCHITECTURE_X86 = 0
582MD_CPU_ARCHITECTURE_ARM = 5
583MD_CPU_ARCHITECTURE_ARM64 = 0x8003
584MD_CPU_ARCHITECTURE_AMD64 = 9
585
586OBJDUMP_BIN = None
587DEFAULT_OBJDUMP_BIN = '/usr/bin/objdump'
588
589class FuncSymbol:
590  def __init__(self, start, size, name):
591    self.start = start
592    self.end = self.start + size
593    self.name = name
594
595  def __cmp__(self, other):
596    if isinstance(other, FuncSymbol):
597      return self.start - other.start
598    return self.start - other
599
600  def Covers(self, addr):
601    return (self.start <= addr) and (addr < self.end)
602
603class MinidumpReader(object):
604  """Minidump (.dmp) reader."""
605
606  _HEADER_MAGIC = 0x504d444d
607
608  def __init__(self, options, minidump_name):
609    self.minidump_name = minidump_name
610    self.minidump_file = open(minidump_name, "r")
611    self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE)
612    self.header = MINIDUMP_HEADER.Read(self.minidump, 0)
613    if self.header.signature != MinidumpReader._HEADER_MAGIC:
614      print >>sys.stderr, "Warning: Unsupported minidump header magic!"
615    DebugPrint(self.header)
616    directories = []
617    offset = self.header.stream_directories_rva
618    for _ in xrange(self.header.stream_count):
619      directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
620      offset += MINIDUMP_DIRECTORY.size
621    self.arch = None
622    self.exception = None
623    self.exception_context = None
624    self.memory_list = None
625    self.memory_list64 = None
626    self.module_list = None
627    self.thread_map = {}
628
629    self.symdir = options.symdir
630    self.modules_with_symbols = []
631    self.symbols = []
632
633    self._ReadArchitecture(directories)
634    self._ReadDirectories(directories)
635    self._FindObjdump(options)
636
637  def _ReadArchitecture(self, directories):
638    # Find MDRawSystemInfo stream and determine arch.
639    for d in directories:
640      if d.stream_type == MD_SYSTEM_INFO_STREAM:
641        system_info = MINIDUMP_RAW_SYSTEM_INFO.Read(
642            self.minidump, d.location.rva)
643        self.arch = system_info.processor_architecture
644        assert self.arch in [MD_CPU_ARCHITECTURE_AMD64,
645                             MD_CPU_ARCHITECTURE_ARM,
646                             MD_CPU_ARCHITECTURE_ARM64,
647                             MD_CPU_ARCHITECTURE_X86]
648    assert not self.arch is None
649
650  def _ReadDirectories(self, directories):
651    for d in directories:
652      DebugPrint(d)
653      if d.stream_type == MD_EXCEPTION_STREAM:
654        self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
655          self.minidump, d.location.rva)
656        DebugPrint(self.exception)
657        self.exception_context = self.ContextDescriptor().Read(
658            self.minidump, self.exception.thread_context.rva)
659        DebugPrint(self.exception_context)
660      elif d.stream_type == MD_THREAD_LIST_STREAM:
661        thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
662        if ctypes.sizeof(thread_list) + 4 == d.location.data_size:
663          thread_list = MINIDUMP_THREAD_LIST_Mac.Read(
664              self.minidump, d.location.rva)
665        assert ctypes.sizeof(thread_list) == d.location.data_size
666        DebugPrint(thread_list)
667        for thread in thread_list.threads:
668          DebugPrint(thread)
669          self.thread_map[thread.id] = thread
670      elif d.stream_type == MD_MODULE_LIST_STREAM:
671        assert self.module_list is None
672        self.module_list = MINIDUMP_MODULE_LIST.Read(
673          self.minidump, d.location.rva)
674        if ctypes.sizeof(self.module_list) + 4 == d.location.data_size:
675          self.module_list = MINIDUMP_MODULE_LIST_Mac.Read(
676              self.minidump, d.location.rva)
677        assert ctypes.sizeof(self.module_list) == d.location.data_size
678        DebugPrint(self.module_list)
679      elif d.stream_type == MD_MEMORY_LIST_STREAM:
680        print >>sys.stderr, "Warning: This is not a full minidump!"
681        assert self.memory_list is None
682        self.memory_list = MINIDUMP_MEMORY_LIST.Read(
683          self.minidump, d.location.rva)
684        if ctypes.sizeof(self.memory_list) + 4 == d.location.data_size:
685          self.memory_list = MINIDUMP_MEMORY_LIST_Mac.Read(
686              self.minidump, d.location.rva)
687        assert ctypes.sizeof(self.memory_list) == d.location.data_size
688        DebugPrint(self.memory_list)
689      elif d.stream_type == MD_MEMORY_64_LIST_STREAM:
690        assert self.memory_list64 is None
691        self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read(
692          self.minidump, d.location.rva)
693        assert ctypes.sizeof(self.memory_list64) == d.location.data_size
694        DebugPrint(self.memory_list64)
695
696  def _FindObjdump(self, options):
697    if options.objdump:
698        objdump_bin = options.objdump
699    else:
700      objdump_bin = self._FindThirdPartyObjdump()
701    if not objdump_bin or not os.path.exists(objdump_bin):
702      print "# Cannot find '%s', falling back to default objdump '%s'" % (
703          objdump_bin, DEFAULT_OBJDUMP_BIN)
704      objdump_bin  = DEFAULT_OBJDUMP_BIN
705    global OBJDUMP_BIN
706    OBJDUMP_BIN = objdump_bin
707    disasm.OBJDUMP_BIN = objdump_bin
708
709  def _FindThirdPartyObjdump(self):
710      # Try to find the platform specific objdump
711      third_party_dir = os.path.join(
712          os.path.dirname(os.path.dirname(__file__)), 'third_party')
713      objdumps = []
714      for root, dirs, files in os.walk(third_party_dir):
715        for file in files:
716          if file.endswith("objdump"):
717            objdumps.append(os.path.join(root, file))
718      if self.arch == MD_CPU_ARCHITECTURE_ARM:
719        platform_filter = 'arm-linux'
720      elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
721        platform_filter = 'aarch64'
722      else:
723        # use default otherwise
724        return None
725      print ("# Looking for platform specific (%s) objdump in "
726             "third_party directory.") % platform_filter
727      objdumps = filter(lambda file: platform_filter in file >= 0, objdumps)
728      if len(objdumps) == 0:
729        print "# Could not find platform specific objdump in third_party."
730        print "# Make sure you installed the correct SDK."
731        return None
732      return objdumps[0]
733
734  def ContextDescriptor(self):
735    if self.arch == MD_CPU_ARCHITECTURE_X86:
736      return MINIDUMP_CONTEXT_X86
737    elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
738      return MINIDUMP_CONTEXT_AMD64
739    elif self.arch == MD_CPU_ARCHITECTURE_ARM:
740      return MINIDUMP_CONTEXT_ARM
741    elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
742      return MINIDUMP_CONTEXT_ARM64
743    else:
744      return None
745
746  def IsValidAlignedAddress(self, address):
747    return self.IsAlignedAddress(address) and self.IsValidAddress(address)
748
749  def IsValidAddress(self, address):
750    return self.FindLocation(address) is not None
751
752  def IsAlignedAddress(self, address):
753    return (address % self.PointerSize()) == 0
754
755  def IsExceptionStackAddress(self, address):
756    if not self.IsAlignedAddress(address): return False
757    return self.IsAnyExceptionStackAddress(address)
758
759  def IsAnyExceptionStackAddress(self, address):
760    return self.StackTop() <= address <= self.StackBottom()
761
762  def IsValidExceptionStackAddress(self, address):
763    if not self.IsValidAddress(address): return False
764    return self.IsExceptionStackAddress(address)
765
766  def IsModuleAddress(self, address):
767    return self.GetModuleForAddress(address) != None
768
769  def GetModuleForAddress(self, address):
770    for module in self.module_list.modules:
771      start = module.base_of_image
772      end = start + module.size_of_image
773      if start <= address < end: return module
774    return None
775
776  def ReadU8(self, address):
777    location = self.FindLocation(address)
778    return ctypes.c_uint8.from_buffer(self.minidump, location).value
779
780  def ReadU32(self, address):
781    location = self.FindLocation(address)
782    return ctypes.c_uint32.from_buffer(self.minidump, location).value
783
784  def ReadU64(self, address):
785    location = self.FindLocation(address)
786    return ctypes.c_uint64.from_buffer(self.minidump, location).value
787
788  def Is64(self):
789    return (self.arch == MD_CPU_ARCHITECTURE_ARM64 or
790            self.arch == MD_CPU_ARCHITECTURE_AMD64)
791
792  def ReadUIntPtr(self, address):
793    if self.Is64():
794      return self.ReadU64(address)
795    return self.ReadU32(address)
796
797  def ReadBytes(self, address, size):
798    location = self.FindLocation(address)
799    return self.minidump[location:location + size]
800
801  def _ReadWord(self, location):
802    if self.Is64():
803      return ctypes.c_uint64.from_buffer(self.minidump, location).value
804    return ctypes.c_uint32.from_buffer(self.minidump, location).value
805
806  def ReadAsciiPtr(self, address):
807    ascii_content = [c if c >= '\x20' and c <  '\x7f' else '.'
808                       for c in self.ReadBytes(address, self.PointerSize())]
809    return ''.join(ascii_content)
810
811  def ReadAsciiString(self, address):
812    string = ""
813    while self.IsValidAddress(address):
814      code = self.ReadU8(address)
815      if 0 < code < 128:
816        string += chr(code)
817      else:
818        break
819      address += 1
820    return string
821
822  def IsProbableASCIIRegion(self, location, length):
823    ascii_bytes = 0
824    non_ascii_bytes = 0
825    for i in xrange(length):
826      loc = location + i
827      byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
828      if byte >= 0x7f:
829        non_ascii_bytes += 1
830      if byte < 0x20 and byte != 0:
831        non_ascii_bytes += 1
832      if byte < 0x7f and byte >= 0x20:
833        ascii_bytes += 1
834      if byte == 0xa:  # newline
835        ascii_bytes += 1
836    if ascii_bytes * 10 <= length:
837      return False
838    if length > 0 and ascii_bytes > non_ascii_bytes * 7:
839      return True
840    if ascii_bytes > non_ascii_bytes * 3:
841      return None  # Maybe
842    return False
843
844  def IsProbableExecutableRegion(self, location, length):
845    opcode_bytes = 0
846    sixty_four = self.Is64()
847    for i in xrange(length):
848      loc = location + i
849      byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
850      if (byte == 0x8b or           # mov
851          byte == 0x89 or           # mov reg-reg
852          (byte & 0xf0) == 0x50 or  # push/pop
853          (sixty_four and (byte & 0xf0) == 0x40) or  # rex prefix
854          byte == 0xc3 or           # return
855          byte == 0x74 or           # jeq
856          byte == 0x84 or           # jeq far
857          byte == 0x75 or           # jne
858          byte == 0x85 or           # jne far
859          byte == 0xe8 or           # call
860          byte == 0xe9 or           # jmp far
861          byte == 0xeb):            # jmp near
862        opcode_bytes += 1
863    opcode_percent = (opcode_bytes * 100) / length
864    threshold = 20
865    if opcode_percent > threshold + 2:
866      return True
867    if opcode_percent > threshold - 2:
868      return None  # Maybe
869    return False
870
871  def FindRegion(self, addr):
872    answer = [-1, -1]
873    def is_in(reader, start, size, location):
874      if addr >= start and addr < start + size:
875        answer[0] = start
876        answer[1] = size
877    self.ForEachMemoryRegion(is_in)
878    if answer[0] == -1:
879      return None
880    return answer
881
882  def ForEachMemoryRegion(self, cb):
883    if self.memory_list64 is not None:
884      for r in self.memory_list64.ranges:
885        location = self.memory_list64.base_rva + offset
886        cb(self, r.start, r.size, location)
887        offset += r.size
888
889    if self.memory_list is not None:
890      for r in self.memory_list.ranges:
891        cb(self, r.start, r.memory.data_size, r.memory.rva)
892
893  def FindWord(self, word, alignment=0):
894    def search_inside_region(reader, start, size, location):
895      location = (location + alignment) & ~alignment
896      for i in xrange(size - self.PointerSize()):
897        loc = location + i
898        if reader._ReadWord(loc) == word:
899          slot = start + (loc - location)
900          print "%s: %s" % (reader.FormatIntPtr(slot),
901                            reader.FormatIntPtr(word))
902    self.ForEachMemoryRegion(search_inside_region)
903
904  def FindWordList(self, word):
905    aligned_res = []
906    unaligned_res = []
907    def search_inside_region(reader, start, size, location):
908      for i in xrange(size - self.PointerSize()):
909        loc = location + i
910        if reader._ReadWord(loc) == word:
911          slot = start + (loc - location)
912          if self.IsAlignedAddress(slot):
913            aligned_res.append(slot)
914          else:
915            unaligned_res.append(slot)
916    self.ForEachMemoryRegion(search_inside_region)
917    return (aligned_res, unaligned_res)
918
919  def FindLocation(self, address):
920    offset = 0
921    if self.memory_list64 is not None:
922      for r in self.memory_list64.ranges:
923        if r.start <= address < r.start + r.size:
924          return self.memory_list64.base_rva + offset + address - r.start
925        offset += r.size
926    if self.memory_list is not None:
927      for r in self.memory_list.ranges:
928        if r.start <= address < r.start + r.memory.data_size:
929          return r.memory.rva + address - r.start
930    return None
931
932  def GetDisasmLines(self, address, size):
933    def CountUndefinedInstructions(lines):
934      pattern = "<UNDEFINED>"
935      return sum([line.count(pattern) for (ignore, line) in lines])
936
937    location = self.FindLocation(address)
938    if location is None: return []
939    arch = None
940    possible_objdump_flags = [""]
941    if self.arch == MD_CPU_ARCHITECTURE_X86:
942      arch = "ia32"
943    elif self.arch == MD_CPU_ARCHITECTURE_ARM:
944      arch = "arm"
945      possible_objdump_flags = ["", "--disassembler-options=force-thumb"]
946    elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
947      arch = "arm64"
948      possible_objdump_flags = ["", "--disassembler-options=force-thumb"]
949    elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
950      arch = "x64"
951    results = [ disasm.GetDisasmLines(self.minidump_name,
952                                     location,
953                                     size,
954                                     arch,
955                                     False,
956                                     objdump_flags)
957                for objdump_flags in possible_objdump_flags ]
958    return min(results, key=CountUndefinedInstructions)
959
960
961  def Dispose(self):
962    self.minidump.close()
963    self.minidump_file.close()
964
965  def ExceptionIP(self):
966    if self.arch == MD_CPU_ARCHITECTURE_AMD64:
967      return self.exception_context.rip
968    elif self.arch == MD_CPU_ARCHITECTURE_ARM:
969      return self.exception_context.pc
970    elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
971      return self.exception_context.pc
972    elif self.arch == MD_CPU_ARCHITECTURE_X86:
973      return self.exception_context.eip
974
975  def ExceptionSP(self):
976    if self.arch == MD_CPU_ARCHITECTURE_AMD64:
977      return self.exception_context.rsp
978    elif self.arch == MD_CPU_ARCHITECTURE_ARM:
979      return self.exception_context.sp
980    elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
981      return self.exception_context.sp
982    elif self.arch == MD_CPU_ARCHITECTURE_X86:
983      return self.exception_context.esp
984
985  def ExceptionFP(self):
986    if self.arch == MD_CPU_ARCHITECTURE_AMD64:
987      return self.exception_context.rbp
988    elif self.arch == MD_CPU_ARCHITECTURE_ARM:
989      return None
990    elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
991      return self.exception_context.fp
992    elif self.arch == MD_CPU_ARCHITECTURE_X86:
993      return self.exception_context.ebp
994
995  def ExceptionThread(self):
996    return self.thread_map[self.exception.thread_id]
997
998  def StackTop(self):
999    return self.ExceptionSP()
1000
1001  def StackBottom(self):
1002    exception_thread = self.ExceptionThread()
1003    return exception_thread.stack.start + \
1004        exception_thread.stack.memory.data_size
1005
1006  def FormatIntPtr(self, value):
1007    if self.Is64():
1008      return "%016x" % value
1009    return "%08x" % value
1010
1011  def PointerSize(self):
1012    if self.Is64():
1013      return 8
1014    return 4
1015
1016  def Register(self, name):
1017    return self.exception_context.__getattribute__(name)
1018
1019  def ReadMinidumpString(self, rva):
1020    string = bytearray(MINIDUMP_STRING.Read(self.minidump, rva).buffer)
1021    string = string.decode("utf16")
1022    return string[0:len(string) - 1]
1023
1024  # Load FUNC records from a BreakPad symbol file
1025  #
1026  #    http://code.google.com/p/google-breakpad/wiki/SymbolFiles
1027  #
1028  def _LoadSymbolsFrom(self, symfile, baseaddr):
1029    print "Loading symbols from %s" % (symfile)
1030    funcs = []
1031    with open(symfile) as f:
1032      for line in f:
1033        result = re.match(
1034            r"^FUNC ([a-f0-9]+) ([a-f0-9]+) ([a-f0-9]+) (.*)$", line)
1035        if result is not None:
1036          start = int(result.group(1), 16)
1037          size = int(result.group(2), 16)
1038          name = result.group(4).rstrip()
1039          bisect.insort_left(self.symbols,
1040                             FuncSymbol(baseaddr + start, size, name))
1041    print " ... done"
1042
1043  def TryLoadSymbolsFor(self, modulename, module):
1044    try:
1045      symfile = os.path.join(self.symdir,
1046                             modulename.replace('.', '_') + ".pdb.sym")
1047      if os.path.isfile(symfile):
1048        self._LoadSymbolsFrom(symfile, module.base_of_image)
1049        self.modules_with_symbols.append(module)
1050    except Exception as e:
1051      print "  ... failure (%s)" % (e)
1052
1053  # Returns true if address is covered by some module that has loaded symbols.
1054  def _IsInModuleWithSymbols(self, addr):
1055    for module in self.modules_with_symbols:
1056      start = module.base_of_image
1057      end = start + module.size_of_image
1058      if (start <= addr) and (addr < end):
1059        return True
1060    return False
1061
1062  # Find symbol covering the given address and return its name in format
1063  #     <symbol name>+<offset from the start>
1064  def FindSymbol(self, addr):
1065    if not self._IsInModuleWithSymbols(addr):
1066      return None
1067
1068    i = bisect.bisect_left(self.symbols, addr)
1069    symbol = None
1070    if (0 < i) and self.symbols[i - 1].Covers(addr):
1071      symbol = self.symbols[i - 1]
1072    elif (i < len(self.symbols)) and self.symbols[i].Covers(addr):
1073      symbol = self.symbols[i]
1074    else:
1075      return None
1076    diff = addr - symbol.start
1077    return "%s+0x%x" % (symbol.name, diff)
1078
1079
1080class Printer(object):
1081  """Printer with indentation support."""
1082
1083  def __init__(self):
1084    self.indent = 0
1085
1086  def Indent(self):
1087    self.indent += 2
1088
1089  def Dedent(self):
1090    self.indent -= 2
1091
1092  def Print(self, string):
1093    print "%s%s" % (self._IndentString(), string)
1094
1095  def PrintLines(self, lines):
1096    indent = self._IndentString()
1097    print "\n".join("%s%s" % (indent, line) for line in lines)
1098
1099  def _IndentString(self):
1100    return self.indent * " "
1101
1102
1103ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
1104
1105
1106def FormatDisasmLine(start, heap, line):
1107  line_address = start + line[0]
1108  stack_slot = heap.stack_map.get(line_address)
1109  marker = "  "
1110  if stack_slot:
1111    marker = "=>"
1112  code = AnnotateAddresses(heap, line[1])
1113
1114  # Compute the actual call target which the disassembler is too stupid
1115  # to figure out (it adds the call offset to the disassembly offset rather
1116  # than the absolute instruction address).
1117  if heap.reader.arch == MD_CPU_ARCHITECTURE_X86:
1118    if code.startswith("e8"):
1119      words = code.split()
1120      if len(words) > 6 and words[5] == "call":
1121        offset = int(words[4] + words[3] + words[2] + words[1], 16)
1122        target = (line_address + offset + 5) & 0xFFFFFFFF
1123        code = code.replace(words[6], "0x%08x" % target)
1124  # TODO(jkummerow): port this hack to ARM and x64.
1125
1126  return "%s%08x %08x: %s" % (marker, line_address, line[0], code)
1127
1128
1129def AnnotateAddresses(heap, line):
1130  extra = []
1131  for m in ADDRESS_RE.finditer(line):
1132    maybe_address = int(m.group(0), 16)
1133    object = heap.FindObject(maybe_address)
1134    if not object: continue
1135    extra.append(str(object))
1136  if len(extra) == 0: return line
1137  return "%s  ;; %s" % (line, ", ".join(extra))
1138
1139
1140class HeapObject(object):
1141  def __init__(self, heap, map, address):
1142    self.heap = heap
1143    self.map = map
1144    self.address = address
1145
1146  def Is(self, cls):
1147    return isinstance(self, cls)
1148
1149  def Print(self, p):
1150    p.Print(str(self))
1151
1152  def __str__(self):
1153    instance_type = "???"
1154    if self.map is not None:
1155      instance_type = INSTANCE_TYPES[self.map.instance_type]
1156    return "%s(%s, %s)" % (self.__class__.__name__,
1157                           self.heap.reader.FormatIntPtr(self.address),
1158                           instance_type)
1159
1160  def ObjectField(self, offset):
1161    field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
1162    return self.heap.FindObjectOrSmi(field_value)
1163
1164  def SmiField(self, offset):
1165    field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
1166    if self.heap.IsSmi(field_value):
1167      return self.heap.SmiUntag(field_value)
1168    return None
1169
1170
1171class Map(HeapObject):
1172  def Decode(self, offset, size, value):
1173    return (value >> offset) & ((1 << size) - 1)
1174
1175  # Instance Sizes
1176  def InstanceSizesOffset(self):
1177    return self.heap.PointerSize()
1178
1179  def InstanceSizeOffset(self):
1180    return self.InstanceSizesOffset()
1181
1182  def InObjectProperties(self):
1183    return self.InstanceSizeOffset() + 1
1184
1185  def UnusedByte(self):
1186    return self.InObjectProperties() + 1
1187
1188  def VisitorId(self):
1189    return self.UnusedByte() + 1
1190
1191  # Instance Attributes
1192  def InstanceAttributesOffset(self):
1193    return self.InstanceSizesOffset() + self.heap.IntSize()
1194
1195  def InstanceTypeOffset(self):
1196    return self.InstanceAttributesOffset()
1197
1198  def BitFieldOffset(self):
1199    return self.InstanceTypeOffset() + 1
1200
1201  def BitField2Offset(self):
1202    return self.BitFieldOffset() + 1
1203
1204  def UnusedPropertyFieldsOffset(self):
1205    return self.BitField2Offset() + 1
1206
1207  # Other fields
1208  def BitField3Offset(self):
1209    return self.InstanceAttributesOffset() + self.heap.IntSize()
1210
1211  def PrototypeOffset(self):
1212    return self.BitField3Offset() + self.heap.PointerSize()
1213
1214  def ConstructorOrBackPointerOffset(self):
1215    return self.PrototypeOffset() + self.heap.PointerSize()
1216
1217  def TransitionsOrPrototypeInfoOffset(self):
1218    return self.ConstructorOrBackPointerOffset() + self.heap.PointerSize()
1219
1220  def DescriptorsOffset(self):
1221    return self.TransitionsOrPrototypeInfoOffset() + self.heap.PointerSize()
1222
1223  def LayoutDescriptorOffset(self):
1224    return self.DescriptorsOffset() + self.heap.PointerSize()
1225
1226  def CodeCacheOffset(self):
1227    if (self.heap.reader.Is64()):
1228      return self.LayoutDescriptorOffset() + self.heap.PointerSize()
1229    return self.DescriptorsOffset() + self.heap.PointerSize()
1230
1231  def DependentCodeOffset(self):
1232    return self.CodeCacheOffset() + self.heap.PointerSize()
1233
1234  def ReadByte(self, offset):
1235    return self.heap.reader.ReadU8(self.address + offset)
1236
1237  def ReadWord(self, offset):
1238    return self.heap.reader.ReadUIntPtr(self.address + offset)
1239
1240  def Print(self, p):
1241    p.Print("Map(%08x)" % (self.address))
1242    p.Print("  - size: %d, inobject: %d, (unused: %d), visitor: %d" % (
1243        self.ReadByte(self.InstanceSizeOffset()),
1244        self.ReadByte(self.InObjectProperties()),
1245        self.ReadByte(self.UnusedByte()),
1246        self.VisitorId()))
1247
1248    instance_type = INSTANCE_TYPES[self.ReadByte(self.InstanceTypeOffset())]
1249    bitfield = self.ReadByte(self.BitFieldOffset())
1250    bitfield2 = self.ReadByte(self.BitField2Offset())
1251    unused = self.ReadByte(self.UnusedPropertyFieldsOffset())
1252    p.Print("  - %s, bf: %d, bf2: %d, unused: %d" % (
1253        instance_type, bitfield, bitfield2, unused))
1254
1255    p.Print("  - kind: %s" % (self.Decode(3, 5, bitfield2)))
1256
1257    bitfield3 = self.ReadWord(self.BitField3Offset())
1258
1259    p.Print(
1260        "  - EnumLength: %d NumberOfOwnDescriptors: %d OwnsDescriptors: %s" % (
1261            self.Decode(0, 10, bitfield3),
1262            self.Decode(10, 10, bitfield3),
1263            self.Decode(21, 1, bitfield3)))
1264    p.Print("  - DictionaryMap: %s" % (self.Decode(20, 1, bitfield3)))
1265    p.Print("  - Deprecated: %s" % (self.Decode(23, 1, bitfield3)))
1266    p.Print("  - IsUnstable: %s" % (self.Decode(24, 1, bitfield3)))
1267    p.Print("  - NewTargetIsBase: %s" % (self.Decode(27, 1, bitfield3)))
1268
1269    descriptors = self.ObjectField(self.DescriptorsOffset())
1270    if descriptors.__class__ == FixedArray:
1271      DescriptorArray(descriptors).Print(p)
1272    else:
1273      p.Print("  - Descriptors: %s" % (descriptors))
1274
1275    transitions = self.ObjectField(self.TransitionsOrPrototypeInfoOffset())
1276    if transitions.__class__ == FixedArray:
1277      TransitionArray(transitions).Print(p)
1278    else:
1279      p.Print("  - TransitionsOrPrototypeInfo: %s" % (transitions))
1280
1281    p.Print("  - Prototype: %s" % self.ObjectField(self.PrototypeOffset()))
1282
1283  def __init__(self, heap, map, address):
1284    HeapObject.__init__(self, heap, map, address)
1285    self.instance_type = \
1286        heap.reader.ReadU8(self.address + self.InstanceTypeOffset())
1287
1288
1289class String(HeapObject):
1290  def LengthOffset(self):
1291    # First word after the map is the hash, the second is the length.
1292    return self.heap.PointerSize() * 2
1293
1294  def __init__(self, heap, map, address):
1295    HeapObject.__init__(self, heap, map, address)
1296    self.length = self.SmiField(self.LengthOffset())
1297
1298  def GetChars(self):
1299    return "?string?"
1300
1301  def Print(self, p):
1302    p.Print(str(self))
1303
1304  def __str__(self):
1305    return "\"%s\"" % self.GetChars()
1306
1307
1308class SeqString(String):
1309  def CharsOffset(self):
1310    return self.heap.PointerSize() * 3
1311
1312  def __init__(self, heap, map, address):
1313    String.__init__(self, heap, map, address)
1314    self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(),
1315                                       self.length)
1316
1317  def GetChars(self):
1318    return self.chars
1319
1320
1321class ExternalString(String):
1322  # TODO(vegorov) fix ExternalString for X64 architecture
1323  RESOURCE_OFFSET = 12
1324
1325  WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
1326  WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
1327
1328  def __init__(self, heap, map, address):
1329    String.__init__(self, heap, map, address)
1330    reader = heap.reader
1331    self.resource = \
1332        reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
1333    self.chars = "?external string?"
1334    if not reader.IsValidAddress(self.resource): return
1335    string_impl_address = self.resource + \
1336        ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
1337    if not reader.IsValidAddress(string_impl_address): return
1338    string_impl = reader.ReadU32(string_impl_address)
1339    chars_ptr_address = string_impl + \
1340        ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
1341    if not reader.IsValidAddress(chars_ptr_address): return
1342    chars_ptr = reader.ReadU32(chars_ptr_address)
1343    if not reader.IsValidAddress(chars_ptr): return
1344    raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
1345    self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
1346
1347  def GetChars(self):
1348    return self.chars
1349
1350
1351class ConsString(String):
1352  def LeftOffset(self):
1353    return self.heap.PointerSize() * 3
1354
1355  def RightOffset(self):
1356    return self.heap.PointerSize() * 4
1357
1358  def __init__(self, heap, map, address):
1359    String.__init__(self, heap, map, address)
1360    self.left = self.ObjectField(self.LeftOffset())
1361    self.right = self.ObjectField(self.RightOffset())
1362
1363  def GetChars(self):
1364    try:
1365      return self.left.GetChars() + self.right.GetChars()
1366    except:
1367      return "***CAUGHT EXCEPTION IN GROKDUMP***"
1368
1369
1370class Oddball(HeapObject):
1371  #Should match declarations in objects.h
1372  KINDS = [
1373    "False",
1374    "True",
1375    "TheHole",
1376    "Null",
1377    "ArgumentMarker",
1378    "Undefined",
1379    "Other"
1380  ]
1381
1382  def ToStringOffset(self):
1383    return self.heap.PointerSize()
1384
1385  def ToNumberOffset(self):
1386    return self.ToStringOffset() + self.heap.PointerSize()
1387
1388  def KindOffset(self):
1389    return self.ToNumberOffset() + self.heap.PointerSize()
1390
1391  def __init__(self, heap, map, address):
1392    HeapObject.__init__(self, heap, map, address)
1393    self.to_string = self.ObjectField(self.ToStringOffset())
1394    self.kind = self.SmiField(self.KindOffset())
1395
1396  def Print(self, p):
1397    p.Print(str(self))
1398
1399  def __str__(self):
1400    if self.to_string:
1401      return "Oddball(%08x, <%s>)" % (self.address, str(self.to_string))
1402    else:
1403      kind = "???"
1404      if 0 <= self.kind < len(Oddball.KINDS):
1405        kind = Oddball.KINDS[self.kind]
1406      return "Oddball(%08x, kind=%s)" % (self.address, kind)
1407
1408
1409class FixedArray(HeapObject):
1410  def LengthOffset(self):
1411    return self.heap.PointerSize()
1412
1413  def ElementsOffset(self):
1414    return self.heap.PointerSize() * 2
1415
1416  def MemberOffset(self, i):
1417    return self.ElementsOffset() + self.heap.PointerSize() * i
1418
1419  def Get(self, i):
1420    return self.ObjectField(self.MemberOffset(i))
1421
1422  def __init__(self, heap, map, address):
1423    HeapObject.__init__(self, heap, map, address)
1424    self.length = self.SmiField(self.LengthOffset())
1425
1426  def Print(self, p):
1427    p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1428    p.Indent()
1429    p.Print("length: %d" % self.length)
1430    base_offset = self.ElementsOffset()
1431    for i in xrange(self.length):
1432      offset = base_offset + 4 * i
1433      try:
1434        p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
1435      except TypeError:
1436        p.Dedent()
1437        p.Print("...")
1438        p.Print("}")
1439        return
1440    p.Dedent()
1441    p.Print("}")
1442
1443  def __str__(self):
1444    return "FixedArray(%08x, length=%d)" % (self.address, self.length)
1445
1446
1447class DescriptorArray(object):
1448  def __init__(self, array):
1449    self.array = array
1450
1451  def Length(self):
1452    return self.array.Get(0)
1453
1454  def Decode(self, offset, size, value):
1455    return (value >> offset) & ((1 << size) - 1)
1456
1457  TYPES = [
1458      "normal",
1459      "field",
1460      "function",
1461      "callbacks"
1462  ]
1463
1464  def Type(self, value):
1465    return DescriptorArray.TYPES[self.Decode(0, 3, value)]
1466
1467  def Attributes(self, value):
1468    attributes = self.Decode(3, 3, value)
1469    result = []
1470    if (attributes & 0): result += ["ReadOnly"]
1471    if (attributes & 1): result += ["DontEnum"]
1472    if (attributes & 2): result += ["DontDelete"]
1473    return "[" + (",".join(result)) + "]"
1474
1475  def Deleted(self, value):
1476    return self.Decode(6, 1, value) == 1
1477
1478  def FieldIndex(self, value):
1479    return self.Decode(20, 11, value)
1480
1481  def Pointer(self, value):
1482    return self.Decode(6, 11, value)
1483
1484  def Details(self, di, value):
1485    return (
1486        di,
1487        self.Type(value),
1488        self.Attributes(value),
1489        self.FieldIndex(value),
1490        self.Pointer(value)
1491    )
1492
1493
1494  def Print(self, p):
1495    length = self.Length()
1496    array = self.array
1497
1498    p.Print("Descriptors(%08x, length=%d)" % (array.address, length))
1499    p.Print("[et] %s" % (array.Get(1)))
1500
1501    for di in xrange(length):
1502      i = 2 + di * 3
1503      p.Print("0x%x" % (array.address + array.MemberOffset(i)))
1504      p.Print("[%i] name:    %s" % (di, array.Get(i + 0)))
1505      p.Print("[%i] details: %s %s field-index %i pointer %i" % \
1506              self.Details(di, array.Get(i + 1)))
1507      p.Print("[%i] value:   %s" % (di, array.Get(i + 2)))
1508
1509    end = self.array.length // 3
1510    if length != end:
1511      p.Print("[%i-%i] slack descriptors" % (length, end))
1512
1513
1514class TransitionArray(object):
1515  def __init__(self, array):
1516    self.array = array
1517
1518  def IsSimpleTransition(self):
1519    return self.array.length <= 2
1520
1521  def Length(self):
1522    # SimpleTransition cases
1523    if self.IsSimpleTransition():
1524      return self.array.length - 1
1525    return (self.array.length - 3) // 2
1526
1527  def Print(self, p):
1528    length = self.Length()
1529    array = self.array
1530
1531    p.Print("Transitions(%08x, length=%d)" % (array.address, length))
1532    p.Print("[backpointer] %s" % (array.Get(0)))
1533    if self.IsSimpleTransition():
1534      if length == 1:
1535        p.Print("[simple target] %s" % (array.Get(1)))
1536      return
1537
1538    elements = array.Get(1)
1539    if elements is not None:
1540      p.Print("[elements   ] %s" % (elements))
1541
1542    prototype = array.Get(2)
1543    if prototype is not None:
1544      p.Print("[prototype  ] %s" % (prototype))
1545
1546    for di in xrange(length):
1547      i = 3 + di * 2
1548      p.Print("[%i] symbol: %s" % (di, array.Get(i + 0)))
1549      p.Print("[%i] target: %s" % (di, array.Get(i + 1)))
1550
1551
1552class JSFunction(HeapObject):
1553  def CodeEntryOffset(self):
1554    return 3 * self.heap.PointerSize()
1555
1556  def SharedOffset(self):
1557    return 5 * self.heap.PointerSize()
1558
1559  def __init__(self, heap, map, address):
1560    HeapObject.__init__(self, heap, map, address)
1561    code_entry = \
1562        heap.reader.ReadU32(self.address + self.CodeEntryOffset())
1563    self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1)
1564    self.shared = self.ObjectField(self.SharedOffset())
1565
1566  def Print(self, p):
1567    source = "\n".join("  %s" % line for line in self._GetSource().split("\n"))
1568    p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1569    p.Indent()
1570    p.Print("inferred name: %s" % self.shared.inferred_name)
1571    if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
1572      p.Print("script name: %s" % self.shared.script.name)
1573    p.Print("source:")
1574    p.PrintLines(self._GetSource().split("\n"))
1575    p.Print("code:")
1576    self.code.Print(p)
1577    if self.code != self.shared.code:
1578      p.Print("unoptimized code:")
1579      self.shared.code.Print(p)
1580    p.Dedent()
1581    p.Print("}")
1582
1583  def __str__(self):
1584    inferred_name = ""
1585    if self.shared is not None and self.shared.Is(SharedFunctionInfo):
1586      inferred_name = self.shared.inferred_name
1587    return "JSFunction(%s, %s) " % \
1588          (self.heap.reader.FormatIntPtr(self.address), inferred_name)
1589
1590  def _GetSource(self):
1591    source = "?source?"
1592    start = self.shared.start_position
1593    end = self.shared.end_position
1594    if not self.shared.script.Is(Script): return source
1595    script_source = self.shared.script.source
1596    if not script_source.Is(String): return source
1597    if start and end:
1598      source = script_source.GetChars()[start:end]
1599    return source
1600
1601
1602class SharedFunctionInfo(HeapObject):
1603  def CodeOffset(self):
1604    return 2 * self.heap.PointerSize()
1605
1606  def ScriptOffset(self):
1607    return 7 * self.heap.PointerSize()
1608
1609  def InferredNameOffset(self):
1610    return 9 * self.heap.PointerSize()
1611
1612  def EndPositionOffset(self):
1613    return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
1614
1615  def StartPositionAndTypeOffset(self):
1616    return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
1617
1618  def __init__(self, heap, map, address):
1619    HeapObject.__init__(self, heap, map, address)
1620    try:
1621      self.code = self.ObjectField(self.CodeOffset())
1622      self.script = self.ObjectField(self.ScriptOffset())
1623      self.inferred_name = self.ObjectField(self.InferredNameOffset())
1624      if heap.PointerSize() == 8:
1625        start_position_and_type = \
1626            heap.reader.ReadU32(self.StartPositionAndTypeOffset())
1627        self.start_position = start_position_and_type >> 2
1628        pseudo_smi_end_position = \
1629            heap.reader.ReadU32(self.EndPositionOffset())
1630        self.end_position = pseudo_smi_end_position >> 2
1631      else:
1632        start_position_and_type = \
1633            self.SmiField(self.StartPositionAndTypeOffset())
1634        if start_position_and_type:
1635          self.start_position = start_position_and_type >> 2
1636        else:
1637          self.start_position = None
1638        self.end_position = \
1639            self.SmiField(self.EndPositionOffset())
1640    except:
1641      print("*** Error while reading SharedFunctionInfo")
1642
1643
1644class Script(HeapObject):
1645  def SourceOffset(self):
1646    return self.heap.PointerSize()
1647
1648  def NameOffset(self):
1649    return self.SourceOffset() + self.heap.PointerSize()
1650
1651  def __init__(self, heap, map, address):
1652    HeapObject.__init__(self, heap, map, address)
1653    self.source = self.ObjectField(self.SourceOffset())
1654    self.name = self.ObjectField(self.NameOffset())
1655
1656
1657class CodeCache(HeapObject):
1658  def DefaultCacheOffset(self):
1659    return self.heap.PointerSize()
1660
1661  def NormalTypeCacheOffset(self):
1662    return self.DefaultCacheOffset() + self.heap.PointerSize()
1663
1664  def __init__(self, heap, map, address):
1665    HeapObject.__init__(self, heap, map, address)
1666    self.default_cache = self.ObjectField(self.DefaultCacheOffset())
1667    self.normal_type_cache = self.ObjectField(self.NormalTypeCacheOffset())
1668
1669  def Print(self, p):
1670    p.Print("CodeCache(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1671    p.Indent()
1672    p.Print("default cache: %s" % self.default_cache)
1673    p.Print("normal type cache: %s" % self.normal_type_cache)
1674    p.Dedent()
1675    p.Print("}")
1676
1677
1678class Code(HeapObject):
1679  CODE_ALIGNMENT_MASK = (1 << 5) - 1
1680
1681  def InstructionSizeOffset(self):
1682    return self.heap.PointerSize()
1683
1684  @staticmethod
1685  def HeaderSize(heap):
1686    return (heap.PointerSize() + heap.IntSize() + \
1687        4 * heap.PointerSize() + 3 * heap.IntSize() + \
1688        Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK
1689
1690  def __init__(self, heap, map, address):
1691    HeapObject.__init__(self, heap, map, address)
1692    self.entry = self.address + Code.HeaderSize(heap)
1693    self.instruction_size = \
1694        heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
1695
1696  def Print(self, p):
1697    lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
1698    p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1699    p.Indent()
1700    p.Print("instruction_size: %d" % self.instruction_size)
1701    p.PrintLines(self._FormatLine(line) for line in lines)
1702    p.Dedent()
1703    p.Print("}")
1704
1705  def _FormatLine(self, line):
1706    return FormatDisasmLine(self.entry, self.heap, line)
1707
1708
1709class V8Heap(object):
1710  CLASS_MAP = {
1711    "SYMBOL_TYPE": SeqString,
1712    "ONE_BYTE_SYMBOL_TYPE": SeqString,
1713    "CONS_SYMBOL_TYPE": ConsString,
1714    "CONS_ONE_BYTE_SYMBOL_TYPE": ConsString,
1715    "EXTERNAL_SYMBOL_TYPE": ExternalString,
1716    "EXTERNAL_SYMBOL_WITH_ONE_BYTE_DATA_TYPE": ExternalString,
1717    "EXTERNAL_ONE_BYTE_SYMBOL_TYPE": ExternalString,
1718    "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString,
1719    "SHORT_EXTERNAL_SYMBOL_WITH_ONE_BYTE_DATA_TYPE": ExternalString,
1720    "SHORT_EXTERNAL_ONE_BYTE_SYMBOL_TYPE": ExternalString,
1721    "STRING_TYPE": SeqString,
1722    "ONE_BYTE_STRING_TYPE": SeqString,
1723    "CONS_STRING_TYPE": ConsString,
1724    "CONS_ONE_BYTE_STRING_TYPE": ConsString,
1725    "EXTERNAL_STRING_TYPE": ExternalString,
1726    "EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE": ExternalString,
1727    "EXTERNAL_ONE_BYTE_STRING_TYPE": ExternalString,
1728    "MAP_TYPE": Map,
1729    "ODDBALL_TYPE": Oddball,
1730    "FIXED_ARRAY_TYPE": FixedArray,
1731    "HASH_TABLE_TYPE": FixedArray,
1732    "OBJECT_BOILERPLATE_DESCRIPTION_TYPE": FixedArray,
1733    "SCOPE_INFO_TYPE": FixedArray,
1734    "JS_FUNCTION_TYPE": JSFunction,
1735    "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
1736    "SCRIPT_TYPE": Script,
1737    "CODE_CACHE_TYPE": CodeCache,
1738    "CODE_TYPE": Code,
1739  }
1740
1741  def __init__(self, reader, stack_map):
1742    self.reader = reader
1743    self.stack_map = stack_map
1744    self.objects = {}
1745
1746  def FindObjectOrSmi(self, tagged_address):
1747    if self.IsSmi(tagged_address): return self.SmiUntag(tagged_address)
1748    return self.FindObject(tagged_address)
1749
1750  def FindObject(self, tagged_address):
1751    if tagged_address in self.objects:
1752      return self.objects[tagged_address]
1753    if not self.IsTaggedObjectAddress(tagged_address): return None
1754    address = tagged_address - 1
1755    if not self.reader.IsValidAddress(address): return None
1756    map_tagged_address = self.reader.ReadUIntPtr(address)
1757    if tagged_address == map_tagged_address:
1758      # Meta map?
1759      meta_map = Map(self, None, address)
1760      instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
1761      if instance_type_name != "MAP_TYPE": return None
1762      meta_map.map = meta_map
1763      object = meta_map
1764    else:
1765      map = self.FindMap(map_tagged_address)
1766      if map is None: return None
1767      instance_type_name = INSTANCE_TYPES.get(map.instance_type)
1768      if instance_type_name is None: return None
1769      cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
1770      object = cls(self, map, address)
1771    self.objects[tagged_address] = object
1772    return object
1773
1774  def FindMap(self, tagged_address):
1775    address = self.FindMapAddress(tagged_address)
1776    if not address: return None
1777    object = Map(self, None, address)
1778    return object
1779
1780  def FindMapAddress(self, tagged_address):
1781    if not self.IsTaggedMapAddress(tagged_address): return None
1782    address = tagged_address - 1
1783    if not self.reader.IsValidAddress(address): return None
1784    return address
1785
1786  def IntSize(self):
1787    return 4
1788
1789  def PointerSize(self):
1790    return self.reader.PointerSize()
1791
1792  def ObjectAlignmentMask(self):
1793    return self.PointerSize() - 1
1794
1795  def IsTaggedObjectAddress(self, address):
1796    return (address & self.ObjectAlignmentMask()) == 1
1797
1798  def IsValidTaggedObjectAddress(self, address):
1799    if not self.IsTaggedObjectAddress(address): return False
1800    return self.reader.IsValidAddress(address)
1801
1802  def IsTaggedMapAddress(self, address):
1803    return (address & self.MapAlignmentMask()) == 1
1804
1805  def MapAlignmentMask(self):
1806    if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64:
1807      return (1 << 4) - 1
1808    elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM:
1809      return (1 << 4) - 1
1810    elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM64:
1811      return (1 << 4) - 1
1812    elif self.reader.arch == MD_CPU_ARCHITECTURE_X86:
1813      return (1 << 5) - 1
1814
1815  def PageAlignmentMask(self):
1816    return (1 << 19) - 1
1817
1818  def IsTaggedAddress(self, address):
1819    return (address & self.ObjectAlignmentMask()) == 1
1820
1821  def IsSmi(self, tagged_address):
1822    if self.reader.Is64():
1823      return (tagged_address & 0xFFFFFFFF) == 0
1824    return not self.IsTaggedAddress(tagged_address)
1825
1826  def SmiUntag(self, tagged_address):
1827    if self.reader.Is64(): return tagged_address >> 32
1828    return tagged_address >> 1
1829
1830  def AddressTypeMarker(self, address):
1831    if not self.reader.IsValidAddress(address): return " "
1832    if self.reader.IsExceptionStackAddress(address): return "S"
1833    if self.reader.IsModuleAddress(address): return "C"
1834    if self.IsTaggedAddress(address):
1835      # Cannot have an tagged pointer into the stack
1836      if self.reader.IsAnyExceptionStackAddress(address): return "s"
1837      return "T"
1838    return "*"
1839
1840  def FormatIntPtr(self, address):
1841    marker = self.AddressTypeMarker(address)
1842    address = self.reader.FormatIntPtr(address)
1843    if marker == " ": return address
1844    return "%s %s" % (address, marker)
1845
1846  def RelativeOffset(self, slot, address):
1847    if not self.reader.IsValidAlignedAddress(slot): return None
1848    if self.IsTaggedObjectAddress(address):
1849      address -= 1
1850    if not self.reader.IsValidAlignedAddress(address): return None
1851    offset = (address - slot) / self.PointerSize()
1852
1853    lower_limit = -32
1854    upper_limit = 128
1855    if self.reader.IsExceptionStackAddress(address):
1856      upper_limit = 0xFFFFFF
1857
1858    if offset < lower_limit or upper_limit < offset: return None
1859    target_address = self.reader.ReadUIntPtr(address)
1860    return "[%+02d]=%s %s" % (offset, self.reader.FormatIntPtr(target_address),
1861                             self.AddressTypeMarker(target_address))
1862
1863  def FindObjectPointers(self, start=0, end=0):
1864    objects = set()
1865    def find_object_in_region(reader, start, size, location):
1866      for slot in range(start, start+size, self.reader.PointerSize()):
1867        if not self.reader.IsValidAddress(slot): break
1868        # Collect only tagged pointers (object) to tagged pointers (map)
1869        tagged_address = self.reader.ReadUIntPtr(slot)
1870        if not self.IsValidTaggedObjectAddress(tagged_address): continue
1871        map_address = self.reader.ReadUIntPtr(tagged_address - 1)
1872        if not self.IsTaggedMapAddress(map_address): continue
1873        objects.add(tagged_address)
1874
1875    if not start and not end:
1876      self.reader.ForEachMemoryRegion(find_object_in_region)
1877    else:
1878      find_object_in_region(self.reader, start, end-start, None)
1879
1880    return objects
1881
1882class KnownObject(HeapObject):
1883  def __init__(self, heap, known_name):
1884    HeapObject.__init__(self, heap, None, None)
1885    self.known_name = known_name
1886
1887  def __str__(self):
1888    return "<%s>" % self.known_name
1889
1890
1891class KnownMap(HeapObject):
1892  def __init__(self, heap, known_name, instance_type):
1893    HeapObject.__init__(self, heap, None, None)
1894    self.instance_type = instance_type
1895    self.known_name = known_name
1896
1897  def __str__(self):
1898    return "<%s>" % self.known_name
1899
1900
1901COMMENT_RE = re.compile(r"^C (0x[0-9a-fA-F]+) (.*)$")
1902PAGEADDRESS_RE = re.compile(
1903    r"^P (mappage|oldpage) (0x[0-9a-fA-F]+)$")
1904
1905
1906class InspectionInfo(object):
1907  def __init__(self, minidump_name, reader):
1908    self.comment_file = minidump_name + ".comments"
1909    self.address_comments = {}
1910    self.page_address = {}
1911    if os.path.exists(self.comment_file):
1912      with open(self.comment_file, "r") as f:
1913        lines = f.readlines()
1914        f.close()
1915
1916        for l in lines:
1917          m = COMMENT_RE.match(l)
1918          if m:
1919            self.address_comments[int(m.group(1), 0)] = m.group(2)
1920          m = PAGEADDRESS_RE.match(l)
1921          if m:
1922            self.page_address[m.group(1)] = int(m.group(2), 0)
1923    self.reader = reader
1924    self.styles = {}
1925    self.color_addresses()
1926    return
1927
1928  def get_page_address(self, page_kind):
1929    return self.page_address.get(page_kind, 0)
1930
1931  def save_page_address(self, page_kind, address):
1932    with open(self.comment_file, "a") as f:
1933      f.write("P %s 0x%x\n" % (page_kind, address))
1934      f.close()
1935
1936  def color_addresses(self):
1937    # Color all stack addresses.
1938    exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
1939    stack_top = self.reader.ExceptionSP()
1940    stack_bottom = exception_thread.stack.start + \
1941        exception_thread.stack.memory.data_size
1942    frame_pointer = self.reader.ExceptionFP()
1943    self.styles[frame_pointer] = "frame"
1944    for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
1945      # stack address
1946      self.styles[slot] = "sa"
1947    for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
1948      maybe_address = self.reader.ReadUIntPtr(slot)
1949      # stack value
1950      self.styles[maybe_address] = "sv"
1951      if slot == frame_pointer:
1952        self.styles[slot] = "frame"
1953        frame_pointer = maybe_address
1954    self.styles[self.reader.ExceptionIP()] = "pc"
1955
1956  def get_style_class(self, address):
1957    return self.styles.get(address, None)
1958
1959  def get_style_class_string(self, address):
1960    style = self.get_style_class(address)
1961    if style != None:
1962      return " class=%s " % style
1963    else:
1964      return ""
1965
1966  def set_comment(self, address, comment):
1967    self.address_comments[address] = comment
1968    with open(self.comment_file, "a") as f:
1969      f.write("C 0x%x %s\n" % (address, comment))
1970      f.close()
1971
1972  def get_comment(self, address):
1973    return self.address_comments.get(address, "")
1974
1975
1976class InspectionPadawan(object):
1977  """The padawan can improve annotations by sensing well-known objects."""
1978  def __init__(self, reader, heap):
1979    self.reader = reader
1980    self.heap = heap
1981    self.known_first_map_page = 0
1982    self.known_first_old_page = 0
1983    self.context = None
1984
1985  def __getattr__(self, name):
1986    """An InspectionPadawan can be used instead of V8Heap, even though
1987       it does not inherit from V8Heap (aka. mixin)."""
1988    return getattr(self.heap, name)
1989
1990  def GetPageOffset(self, tagged_address):
1991    return tagged_address & self.heap.PageAlignmentMask()
1992
1993  def IsInKnownMapSpace(self, tagged_address):
1994    page_address = tagged_address & ~self.heap.PageAlignmentMask()
1995    return page_address == self.known_first_map_page
1996
1997  def IsInKnownOldSpace(self, tagged_address):
1998    page_address = tagged_address & ~self.heap.PageAlignmentMask()
1999    return page_address == self.known_first_old_page
2000
2001  def ContainingKnownOldSpaceName(self, tagged_address):
2002    page_address = tagged_address & ~self.heap.PageAlignmentMask()
2003    if page_address == self.known_first_old_page: return "OLD_SPACE"
2004    return None
2005
2006  def FrameMarkerName(self, value):
2007    # The frame marker is Smi-tagged but not Smi encoded and 0 is not a valid
2008    # frame type.
2009    value = (value >> 1) - 1
2010    if 0 <= value < len(FRAME_MARKERS):
2011      return "Possibly %s frame marker" % FRAME_MARKERS[value]
2012    return None
2013
2014  def IsFrameMarker(self, slot, address):
2015    if not slot: return False
2016    # Frame markers only occur directly after a frame pointer and only on the
2017    # stack.
2018    if not self.reader.IsExceptionStackAddress(slot): return False
2019    next_slot = slot + self.reader.PointerSize()
2020    if not self.reader.IsValidAddress(next_slot): return False
2021    next_address = self.reader.ReadUIntPtr(next_slot)
2022    return self.reader.IsExceptionStackAddress(next_address)
2023
2024  def FormatSmi(self, address):
2025    value = self.heap.SmiUntag(address)
2026    # On 32-bit systems almost everything looks like a Smi.
2027    if not self.reader.Is64() or value == 0: return None
2028    return "Smi(%d)" % value
2029
2030  def SenseObject(self, address, slot=None):
2031    if self.IsFrameMarker(slot, address):
2032      return self.FrameMarkerName(address)
2033    if self.heap.IsSmi(address):
2034      return self.FormatSmi(address)
2035    if not self.heap.IsTaggedAddress(address): return None
2036    tagged_address = address
2037    if self.IsInKnownOldSpace(tagged_address):
2038      offset = self.GetPageOffset(tagged_address)
2039      lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset)
2040      known_obj_name = KNOWN_OBJECTS.get(lookup_key)
2041      if known_obj_name:
2042        return KnownObject(self, known_obj_name)
2043    if self.IsInKnownMapSpace(tagged_address):
2044      known_map = self.SenseMap(tagged_address)
2045      if known_map:
2046        return known_map
2047    found_obj = self.heap.FindObject(tagged_address)
2048    if found_obj: return found_obj
2049    address = tagged_address - 1
2050    if self.reader.IsValidAddress(address):
2051      map_tagged_address = self.reader.ReadUIntPtr(address)
2052      map = self.SenseMap(map_tagged_address)
2053      if map is None: return None
2054      instance_type_name = INSTANCE_TYPES.get(map.instance_type)
2055      if instance_type_name is None: return None
2056      cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
2057      return cls(self, map, address)
2058    return None
2059
2060  def SenseMap(self, tagged_address):
2061    if self.IsInKnownMapSpace(tagged_address):
2062      offset = self.GetPageOffset(tagged_address)
2063      lookup_key = ("MAP_SPACE", offset)
2064      known_map_info = KNOWN_MAPS.get(lookup_key)
2065      if known_map_info:
2066        known_map_type, known_map_name = known_map_info
2067        return KnownMap(self, known_map_name, known_map_type)
2068    found_map = self.heap.FindMap(tagged_address)
2069    if found_map: return found_map
2070    return None
2071
2072  def FindObjectOrSmi(self, tagged_address):
2073    """When used as a mixin in place of V8Heap."""
2074    found_obj = self.SenseObject(tagged_address)
2075    if found_obj: return found_obj
2076    if self.IsSmi(tagged_address):
2077      return self.FormatSmi(tagged_address)
2078    else:
2079      return "Unknown(%s)" % self.reader.FormatIntPtr(tagged_address)
2080
2081  def FindObject(self, tagged_address):
2082    """When used as a mixin in place of V8Heap."""
2083    raise NotImplementedError
2084
2085  def FindMap(self, tagged_address):
2086    """When used as a mixin in place of V8Heap."""
2087    raise NotImplementedError
2088
2089  def PrintKnowledge(self):
2090    print "  known_first_map_page = %s\n"\
2091          "  known_first_old_page = %s" % (
2092          self.reader.FormatIntPtr(self.known_first_map_page),
2093          self.reader.FormatIntPtr(self.known_first_old_page))
2094
2095  def FindFirstAsciiString(self, start, end=None, min_length=32):
2096    """ Walk the memory until we find a large string """
2097    if not end: end = start + 64
2098    for slot in xrange(start, end):
2099      if not self.reader.IsValidAddress(slot): break
2100      message = self.reader.ReadAsciiString(slot)
2101      if len(message) > min_length:
2102        return (slot, message)
2103    return (None,None)
2104
2105  def PrintStackTraceMessage(self, start=None, print_message=True):
2106    """
2107    Try to print a possible message from PushStackTraceAndDie.
2108    Returns the first address where the normal stack starts again.
2109    """
2110    # Only look at the first 1k words on the stack
2111    ptr_size = self.reader.PointerSize()
2112    if start is None: start = self.reader.ExceptionSP()
2113    if not self.reader.IsValidAddress(start): return start
2114    end = start + ptr_size * 1024 * 4
2115    magic1 = None
2116    for slot in xrange(start, end, ptr_size):
2117      if not self.reader.IsValidAddress(slot + ptr_size): break
2118      magic1 = self.reader.ReadUIntPtr(slot)
2119      magic2 = self.reader.ReadUIntPtr(slot + ptr_size)
2120      pair = (magic1 & 0xFFFFFFFF, magic2 & 0xFFFFFFFF)
2121      if pair in MAGIC_MARKER_PAIRS:
2122        return self.TryExtractOldStyleStackTrace(slot, start, end,
2123                                                 print_message)
2124      if pair[0] == STACK_TRACE_MARKER:
2125        return self.TryExtractStackTrace(slot, start, end, print_message)
2126      elif pair[0] == ERROR_MESSAGE_MARKER:
2127        return self.TryExtractErrorMessage(slot, start, end, print_message)
2128    # Simple fallback in case not stack trace object was found
2129    return self.TryExtractOldStyleStackTrace(0, start, end,
2130                                             print_message)
2131
2132  def TryExtractStackTrace(self, slot, start, end, print_message):
2133    ptr_size = self.reader.PointerSize()
2134    assert self.reader.ReadUIntPtr(slot) & 0xFFFFFFFF == STACK_TRACE_MARKER
2135    end_marker = STACK_TRACE_MARKER + 1;
2136    header_size = 10
2137    # Look for the end marker after the fields and the message buffer.
2138    end_search = start + (32 * 1024) + (header_size * ptr_size);
2139    end_slot = self.FindPtr(end_marker, end_search, end_search + ptr_size * 512)
2140    if not end_slot: return start
2141    print "Stack Message (start=%s):" % self.heap.FormatIntPtr(slot)
2142    slot += ptr_size
2143    for name in ("isolate","ptr1", "ptr2", "ptr3", "ptr4", "codeObject1",
2144                 "codeObject2", "codeObject3", "codeObject4"):
2145      value = self.reader.ReadUIntPtr(slot)
2146      print " %s: %s" % (name.rjust(14), self.heap.FormatIntPtr(value))
2147      slot += ptr_size
2148    print "  message start: %s" % self.heap.FormatIntPtr(slot)
2149    stack_start = end_slot + ptr_size
2150    print "  stack_start:   %s" % self.heap.FormatIntPtr(stack_start)
2151    (message_start, message) = self.FindFirstAsciiString(slot)
2152    self.FormatStackTrace(message, print_message)
2153    return stack_start
2154
2155  def FindPtr(self, expected_value, start, end):
2156    ptr_size = self.reader.PointerSize()
2157    for slot in xrange(start, end, ptr_size):
2158      if not self.reader.IsValidAddress(slot): return None
2159      value = self.reader.ReadUIntPtr(slot)
2160      if value == expected_value: return slot
2161    return None
2162
2163  def TryExtractErrorMessage(self, slot, start, end, print_message):
2164    ptr_size = self.reader.PointerSize()
2165    end_marker = ERROR_MESSAGE_MARKER + 1;
2166    header_size = 1
2167    end_search = start + 1024 + (header_size * ptr_size);
2168    end_slot = self.FindPtr(end_marker, end_search, end_search + ptr_size * 512)
2169    if not end_slot: return start
2170    print "Error Message (start=%s):" % self.heap.FormatIntPtr(slot)
2171    slot += ptr_size
2172    (message_start, message) = self.FindFirstAsciiString(slot)
2173    self.FormatStackTrace(message, print_message)
2174    stack_start = end_slot + ptr_size
2175    return stack_start
2176
2177  def TryExtractOldStyleStackTrace(self, message_slot, start, end,
2178                                   print_message):
2179    ptr_size = self.reader.PointerSize()
2180    if message_slot == 0:
2181      """
2182      On Mac we don't always get proper magic markers, so just try printing
2183      the first long ascii string found on the stack.
2184      """
2185      magic1 = None
2186      magic2 = None
2187      message_start, message = self.FindFirstAsciiString(start, end, 128)
2188      if message_start is None: return start
2189    else:
2190      message_start = self.reader.ReadUIntPtr(message_slot + ptr_size * 4)
2191      message = self.reader.ReadAsciiString(message_start)
2192    stack_start = message_start + len(message) + 1
2193    # Make sure the address is word aligned
2194    stack_start =  stack_start - (stack_start % ptr_size)
2195    if magic1 is None:
2196      print "Stack Message:"
2197      print "  message start: %s" % self.heap.FormatIntPtr(message_start)
2198      print "  stack_start:   %s" % self.heap.FormatIntPtr(stack_start )
2199    else:
2200      ptr1 = self.reader.ReadUIntPtr(slot + ptr_size * 2)
2201      ptr2 = self.reader.ReadUIntPtr(slot + ptr_size * 3)
2202      print "Stack Message:"
2203      print "  magic1:        %s" % self.heap.FormatIntPtr(magic1)
2204      print "  magic2:        %s" % self.heap.FormatIntPtr(magic2)
2205      print "  ptr1:          %s" % self.heap.FormatIntPtr(ptr1)
2206      print "  ptr2:          %s" % self.heap.FormatIntPtr(ptr2)
2207      print "  message start: %s" % self.heap.FormatIntPtr(message_start)
2208      print "  stack_start:   %s" % self.heap.FormatIntPtr(stack_start )
2209      print ""
2210    self.FormatStackTrace(message, print_message)
2211    return stack_start
2212
2213  def FormatStackTrace(self, message, print_message):
2214    if not print_message:
2215      print "  Use `dsa` to print the message with annotated addresses."
2216      print ""
2217      return
2218    ptr_size = self.reader.PointerSize()
2219    # Annotate all addresses in the dumped message
2220    prog = re.compile("[0-9a-fA-F]{%s}" % ptr_size*2)
2221    addresses = list(set(prog.findall(message)))
2222    for i in range(len(addresses)):
2223      address_org = addresses[i]
2224      address = self.heap.FormatIntPtr(int(address_org, 16))
2225      if address_org != address:
2226        message = message.replace(address_org, address)
2227    print "Message:"
2228    print "="*80
2229    print message
2230    print "="*80
2231    print ""
2232
2233
2234  def TryInferFramePointer(self, slot, address):
2235    """ Assume we have a framepointer if we find 4 consecutive links """
2236    for i in range(0, 4):
2237      if not self.reader.IsExceptionStackAddress(address): return 0
2238      next_address = self.reader.ReadUIntPtr(address)
2239      if next_address == address: return 0
2240      address = next_address
2241    return slot
2242
2243  def TryInferContext(self, address):
2244    if self.context: return
2245    ptr_size = self.reader.PointerSize()
2246    possible_context = dict()
2247    count = 0
2248    while self.reader.IsExceptionStackAddress(address):
2249      prev_addr = self.reader.ReadUIntPtr(address-ptr_size)
2250      if self.heap.IsTaggedObjectAddress(prev_addr):
2251        if prev_addr in possible_context:
2252          possible_context[prev_addr] += 1
2253        else:
2254          possible_context[prev_addr] = 1
2255      address = self.reader.ReadUIntPtr(address)
2256      count += 1
2257    if count <= 5 or len(possible_context) == 0: return
2258    # Find entry with highest count
2259    possible_context = possible_context.items()
2260    possible_context.sort(key=lambda pair: pair[1])
2261    address,count = possible_context[-1]
2262    if count <= 4: return
2263    self.context = address
2264
2265  def InterpretMemory(self, start, end):
2266    # On 64 bit we omit frame pointers, so we have to do some more guesswork.
2267    frame_pointer = 0
2268    if not self.reader.Is64():
2269      frame_pointer = self.reader.ExceptionFP()
2270      # Follow the framepointer into the address range
2271      while frame_pointer and frame_pointer < start:
2272        frame_pointer = self.reader.ReadUIntPtr(frame_pointer)
2273        if not self.reader.IsExceptionStackAddress(frame_pointer) or \
2274            not frame_pointer:
2275          frame_pointer = 0
2276          break
2277    in_oom_dump_area  = False
2278    is_stack = self.reader.IsExceptionStackAddress(start)
2279    free_space_end = 0
2280    ptr_size = self.reader.PointerSize()
2281
2282    for slot in xrange(start, end, ptr_size):
2283      if not self.reader.IsValidAddress(slot):
2284        print "%s: Address is not contained within the minidump!" % slot
2285        return
2286      maybe_address = self.reader.ReadUIntPtr(slot)
2287      address_info = []
2288      # Mark continuous free space objects
2289      if slot == free_space_end:
2290        address_info.append("+")
2291      elif slot <= free_space_end:
2292        address_info.append("|")
2293      else:
2294        free_space_end = 0
2295
2296      heap_object = self.SenseObject(maybe_address, slot)
2297      if heap_object:
2298        # Detect Free-space ranges
2299        if isinstance(heap_object, KnownMap) and \
2300            heap_object.known_name == "FreeSpaceMap":
2301          # The free-space length is is stored as a Smi in the next slot.
2302          length = self.reader.ReadUIntPtr(slot + ptr_size)
2303          if self.heap.IsSmi(length):
2304            length = self.heap.SmiUntag(length)
2305            free_space_end = slot + length - ptr_size
2306        address_info.append(str(heap_object))
2307      relative_offset = self.heap.RelativeOffset(slot, maybe_address)
2308      if relative_offset:
2309        address_info.append(relative_offset)
2310      if maybe_address == self.context:
2311        address_info.append("CONTEXT")
2312
2313      maybe_address_contents = None
2314      if is_stack:
2315        if self.reader.IsExceptionStackAddress(maybe_address):
2316          maybe_address_contents = \
2317              self.reader.ReadUIntPtr(maybe_address) & 0xFFFFFFFF
2318          if maybe_address_contents == 0xdecade00:
2319            in_oom_dump_area = True
2320          if frame_pointer == 0:
2321            frame_pointer = self.TryInferFramePointer(slot, maybe_address)
2322            if frame_pointer != 0:
2323              self.TryInferContext(slot)
2324        maybe_symbol = self.reader.FindSymbol(maybe_address)
2325        if in_oom_dump_area:
2326          if maybe_address_contents == 0xdecade00:
2327            address_info = ["<==== HeapStats start marker"]
2328          elif maybe_address_contents == 0xdecade01:
2329            address_info = ["<==== HeapStats end marker"]
2330          elif maybe_address_contents is not None:
2331            address_info = [" %d (%d Mbytes)" % (maybe_address_contents,
2332                                                 maybe_address_contents >> 20)]
2333        if slot == frame_pointer:
2334          if not self.reader.IsExceptionStackAddress(maybe_address):
2335            address_info.append("<==== BAD frame pointer")
2336            frame_pointer = 0
2337          else:
2338            address_info.append("<==== Frame pointer")
2339          frame_pointer = maybe_address
2340      address_type_marker = self.heap.AddressTypeMarker(maybe_address)
2341      string_value = self.reader.ReadAsciiPtr(slot)
2342      print "%s: %s %s %s %s" % (self.reader.FormatIntPtr(slot),
2343                           self.reader.FormatIntPtr(maybe_address),
2344                           address_type_marker,
2345                           string_value,
2346                           ' | '.join(address_info))
2347      if maybe_address_contents == 0xdecade01:
2348        in_oom_dump_area = False
2349      heap_object = self.heap.FindObject(maybe_address)
2350      if heap_object:
2351        heap_object.Print(Printer())
2352        print ""
2353
2354WEB_HEADER = """
2355<!DOCTYPE html>
2356<html>
2357<head>
2358<meta content="text/html; charset=utf-8" http-equiv="content-type">
2359<style media="screen" type="text/css">
2360
2361.code {
2362  font-family: monospace;
2363}
2364
2365.dmptable {
2366  border-collapse : collapse;
2367  border-spacing : 0px;
2368  table-layout: fixed;
2369}
2370
2371.codedump {
2372  border-collapse : collapse;
2373  border-spacing : 0px;
2374  table-layout: fixed;
2375}
2376
2377.addrcomments {
2378  border : 0px;
2379}
2380
2381.register {
2382  padding-right : 1em;
2383}
2384
2385.header {
2386  clear : both;
2387}
2388
2389.header .navigation {
2390  float : left;
2391}
2392
2393.header .dumpname {
2394  float : right;
2395}
2396
2397tr.highlight-line {
2398  background-color : yellow;
2399}
2400
2401.highlight {
2402  background-color : magenta;
2403}
2404
2405tr.inexact-highlight-line {
2406  background-color : pink;
2407}
2408
2409input {
2410  background-color: inherit;
2411  border: 1px solid LightGray;
2412}
2413
2414.dumpcomments {
2415  border : 1px solid LightGray;
2416  width : 32em;
2417}
2418
2419.regions td {
2420  padding:0 15px 0 15px;
2421}
2422
2423.stackframe td {
2424  background-color : cyan;
2425}
2426
2427.stackaddress, .sa {
2428  background-color : LightGray;
2429}
2430
2431.stackval, .sv {
2432  background-color : LightCyan;
2433}
2434
2435.frame {
2436  background-color : cyan;
2437}
2438
2439.commentinput, .ci {
2440  width : 20em;
2441}
2442
2443/* a.nodump */
2444a.nd:visited {
2445  color : black;
2446  text-decoration : none;
2447}
2448
2449a.nd:link {
2450  color : black;
2451  text-decoration : none;
2452}
2453
2454a:visited {
2455  color : blueviolet;
2456}
2457
2458a:link {
2459  color : blue;
2460}
2461
2462.disasmcomment {
2463  color : DarkGreen;
2464}
2465
2466</style>
2467
2468<script type="application/javascript">
2469
2470var address_str = "address-";
2471var address_len = address_str.length;
2472
2473function comment() {
2474  var s = event.srcElement.id;
2475  var index = s.indexOf(address_str);
2476  if (index >= 0) {
2477    send_comment(s.substring(index + address_len), event.srcElement.value);
2478  }
2479}
2480var c = comment;
2481
2482function send_comment(address, comment) {
2483  xmlhttp = new XMLHttpRequest();
2484  address = encodeURIComponent(address)
2485  comment = encodeURIComponent(comment)
2486  xmlhttp.open("GET",
2487      "setcomment?%(query_dump)s&address=" + address +
2488      "&comment=" + comment, true);
2489  xmlhttp.send();
2490}
2491
2492var dump_str = "dump-";
2493var dump_len = dump_str.length;
2494
2495function dump_comment() {
2496  var s = event.srcElement.id;
2497  var index = s.indexOf(dump_str);
2498  if (index >= 0) {
2499    send_dump_desc(s.substring(index + dump_len), event.srcElement.value);
2500  }
2501}
2502
2503function send_dump_desc(name, desc) {
2504  xmlhttp = new XMLHttpRequest();
2505  name = encodeURIComponent(name)
2506  desc = encodeURIComponent(desc)
2507  xmlhttp.open("GET",
2508      "setdumpdesc?dump=" + name +
2509      "&description=" + desc, true);
2510  xmlhttp.send();
2511}
2512
2513function onpage(kind, address) {
2514  xmlhttp = new XMLHttpRequest();
2515  kind = encodeURIComponent(kind)
2516  address = encodeURIComponent(address)
2517  xmlhttp.onreadystatechange = function() {
2518    if (xmlhttp.readyState==4 && xmlhttp.status==200) {
2519      location.reload(true)
2520    }
2521  };
2522  xmlhttp.open("GET",
2523      "setpageaddress?%(query_dump)s&kind=" + kind +
2524      "&address=" + address);
2525  xmlhttp.send();
2526}
2527
2528</script>
2529
2530<title>Dump %(dump_name)s</title>
2531</head>
2532
2533<body>
2534  <div class="header">
2535    <form class="navigation" action="search.html">
2536      <a href="summary.html?%(query_dump)s">Context info</a>&nbsp;&nbsp;&nbsp;
2537      <a href="info.html?%(query_dump)s">Dump info</a>&nbsp;&nbsp;&nbsp;
2538      <a href="modules.html?%(query_dump)s">Modules</a>&nbsp;&nbsp;&nbsp;
2539      &nbsp;
2540      <input type="search" name="val">
2541      <input type="submit" name="search" value="Search">
2542      <input type="hidden" name="dump" value="%(dump_name)s">
2543    </form>
2544    <form class="navigation" action="disasm.html#highlight">
2545      &nbsp;
2546      &nbsp;
2547      &nbsp;
2548      <input type="search" name="val">
2549      <input type="submit" name="disasm" value="Disasm">
2550      &nbsp;
2551      &nbsp;
2552      &nbsp;
2553      <a href="dumps.html">Dumps...</a>
2554    </form>
2555  </div>
2556  <br>
2557  <hr>
2558"""
2559
2560
2561WEB_FOOTER = """
2562</body>
2563</html>
2564"""
2565
2566
2567class WebParameterError(Exception):
2568  def __init__(self, message):
2569    Exception.__init__(self, message)
2570
2571
2572class InspectionWebHandler(BaseHTTPServer.BaseHTTPRequestHandler):
2573  def formatter(self, query_components):
2574    name = query_components.get("dump", [None])[0]
2575    return self.server.get_dump_formatter(name)
2576
2577  def send_success_html_headers(self):
2578    self.send_response(200)
2579    self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
2580    self.send_header("Pragma", "no-cache")
2581    self.send_header("Expires", "0")
2582    self.send_header('Content-type','text/html')
2583    self.end_headers()
2584    return
2585
2586  def do_GET(self):
2587    try:
2588      parsedurl = urlparse.urlparse(self.path)
2589      query_components = urlparse.parse_qs(parsedurl.query)
2590      if parsedurl.path == "/dumps.html":
2591        self.send_success_html_headers()
2592        out_buffer = StringIO.StringIO()
2593        self.server.output_dumps(out_buffer)
2594        self.wfile.write(out_buffer.getvalue())
2595      elif parsedurl.path == "/summary.html":
2596        self.send_success_html_headers()
2597        out_buffer = StringIO.StringIO()
2598        self.formatter(query_components).output_summary(out_buffer)
2599        self.wfile.write(out_buffer.getvalue())
2600      elif parsedurl.path == "/info.html":
2601        self.send_success_html_headers()
2602        out_buffer = StringIO.StringIO()
2603        self.formatter(query_components).output_info(out_buffer)
2604        self.wfile.write(out_buffer.getvalue())
2605      elif parsedurl.path == "/modules.html":
2606        self.send_success_html_headers()
2607        out_buffer = StringIO.StringIO()
2608        self.formatter(query_components).output_modules(out_buffer)
2609        self.wfile.write(out_buffer.getvalue())
2610      elif parsedurl.path == "/search.html" or parsedurl.path == "/s":
2611        address = query_components.get("val", [])
2612        if len(address) != 1:
2613          self.send_error(404, "Invalid params")
2614          return
2615        self.send_success_html_headers()
2616        out_buffer = StringIO.StringIO()
2617        self.formatter(query_components).output_search_res(
2618            out_buffer, address[0])
2619        self.wfile.write(out_buffer.getvalue())
2620      elif parsedurl.path == "/disasm.html":
2621        address = query_components.get("val", [])
2622        exact = query_components.get("exact", ["on"])
2623        if len(address) != 1:
2624          self.send_error(404, "Invalid params")
2625          return
2626        self.send_success_html_headers()
2627        out_buffer = StringIO.StringIO()
2628        self.formatter(query_components).output_disasm(
2629            out_buffer, address[0], exact[0])
2630        self.wfile.write(out_buffer.getvalue())
2631      elif parsedurl.path == "/data.html":
2632        address = query_components.get("val", [])
2633        datakind = query_components.get("type", ["address"])
2634        if len(address) == 1 and len(datakind) == 1:
2635          self.send_success_html_headers()
2636          out_buffer = StringIO.StringIO()
2637          self.formatter(query_components).output_data(
2638              out_buffer, address[0], datakind[0])
2639          self.wfile.write(out_buffer.getvalue())
2640        else:
2641          self.send_error(404,'Invalid params')
2642      elif parsedurl.path == "/setdumpdesc":
2643        name = query_components.get("dump", [""])
2644        description = query_components.get("description", [""])
2645        if len(name) == 1 and len(description) == 1:
2646          name = name[0]
2647          description = description[0]
2648          if self.server.set_dump_desc(name, description):
2649            self.send_success_html_headers()
2650            self.wfile.write("OK")
2651            return
2652        self.send_error(404,'Invalid params')
2653      elif parsedurl.path == "/setcomment":
2654        address = query_components.get("address", [])
2655        comment = query_components.get("comment", [""])
2656        if len(address) == 1 and len(comment) == 1:
2657          address = address[0]
2658          comment = comment[0]
2659          self.formatter(query_components).set_comment(address, comment)
2660          self.send_success_html_headers()
2661          self.wfile.write("OK")
2662        else:
2663          self.send_error(404,'Invalid params')
2664      elif parsedurl.path == "/setpageaddress":
2665        kind = query_components.get("kind", [])
2666        address = query_components.get("address", [""])
2667        if len(kind) == 1 and len(address) == 1:
2668          kind = kind[0]
2669          address = address[0]
2670          self.formatter(query_components).set_page_address(kind, address)
2671          self.send_success_html_headers()
2672          self.wfile.write("OK")
2673        else:
2674          self.send_error(404,'Invalid params')
2675      else:
2676        self.send_error(404,'File Not Found: %s' % self.path)
2677
2678    except IOError:
2679      self.send_error(404,'File Not Found: %s' % self.path)
2680
2681    except WebParameterError as e:
2682      self.send_error(404, 'Web parameter error: %s' % e.message)
2683
2684
2685HTML_REG_FORMAT = "<span class=\"register\"><b>%s</b>:&nbsp;%s</span><br/>\n"
2686
2687
2688class InspectionWebFormatter(object):
2689  CONTEXT_FULL = 0
2690  CONTEXT_SHORT = 1
2691
2692  def __init__(self, switches, minidump_name, http_server):
2693    self.dumpfilename = os.path.split(minidump_name)[1]
2694    self.encfilename = urllib.urlencode({ 'dump' : self.dumpfilename })
2695    self.reader = MinidumpReader(switches, minidump_name)
2696    self.server = http_server
2697
2698    # Set up the heap
2699    exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
2700    stack_top = self.reader.ExceptionSP()
2701    stack_bottom = exception_thread.stack.start + \
2702        exception_thread.stack.memory.data_size
2703    stack_map = {self.reader.ExceptionIP(): -1}
2704    for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
2705      maybe_address = self.reader.ReadUIntPtr(slot)
2706      if not maybe_address in stack_map:
2707        stack_map[maybe_address] = slot
2708    self.heap = V8Heap(self.reader, stack_map)
2709
2710    self.padawan = InspectionPadawan(self.reader, self.heap)
2711    self.comments = InspectionInfo(minidump_name, self.reader)
2712    self.padawan.known_first_old_page = (
2713        self.comments.get_page_address("oldpage"))
2714    self.padawan.known_first_map_page = (
2715        self.comments.get_page_address("mappage"))
2716
2717  def set_comment(self, straddress, comment):
2718    try:
2719      address = int(straddress, 0)
2720      self.comments.set_comment(address, comment)
2721    except ValueError:
2722      print "Invalid address"
2723
2724  def set_page_address(self, kind, straddress):
2725    try:
2726      address = int(straddress, 0)
2727      if kind == "oldpage":
2728        self.padawan.known_first_old_page = address
2729      elif kind == "mappage":
2730        self.padawan.known_first_map_page = address
2731      self.comments.save_page_address(kind, address)
2732    except ValueError:
2733      print "Invalid address"
2734
2735  def td_from_address(self, f, address):
2736    f.write("<td %s>" % self.comments.get_style_class_string(address))
2737
2738  def format_address(self, maybeaddress, straddress = None):
2739    if maybeaddress is None:
2740      return "not in dump"
2741    else:
2742      if straddress is None:
2743        straddress = "0x" + self.reader.FormatIntPtr(maybeaddress)
2744      style_class = ""
2745      if not self.reader.IsValidAddress(maybeaddress):
2746        style_class = "class=nd"
2747      return ("<a %s href=s?%s&amp;val=%s>%s</a>" %
2748              (style_class, self.encfilename, straddress, straddress))
2749
2750  def output_header(self, f):
2751    f.write(WEB_HEADER %
2752        { "query_dump" : self.encfilename,
2753          "dump_name"  : cgi.escape(self.dumpfilename) })
2754
2755  def output_footer(self, f):
2756    f.write(WEB_FOOTER)
2757
2758  MAX_CONTEXT_STACK = 2048
2759
2760  def output_summary(self, f):
2761    self.output_header(f)
2762    f.write('<div class="code">')
2763    self.output_context(f, InspectionWebFormatter.CONTEXT_SHORT)
2764    self.output_disasm_pc(f)
2765
2766    # Output stack
2767    exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
2768    stack_top = self.reader.ExceptionSP()
2769    stack_bottom = min(exception_thread.stack.start + \
2770        exception_thread.stack.memory.data_size,
2771        stack_top + self.MAX_CONTEXT_STACK)
2772    self.output_words(f, stack_top - 16, stack_bottom, stack_top, "Stack")
2773
2774    f.write('</div>')
2775    self.output_footer(f)
2776    return
2777
2778  def output_info(self, f):
2779    self.output_header(f)
2780    f.write("<h3>Dump info</h3>")
2781    f.write("Description: ")
2782    self.server.output_dump_desc_field(f, self.dumpfilename)
2783    f.write("<br>")
2784    f.write("Filename: ")
2785    f.write("<span class=\"code\">%s</span><br>" % (self.dumpfilename))
2786    dt = datetime.datetime.fromtimestamp(self.reader.header.time_date_stampt)
2787    f.write("Timestamp: %s<br>" % dt.strftime('%Y-%m-%d %H:%M:%S'))
2788    self.output_context(f, InspectionWebFormatter.CONTEXT_FULL)
2789    self.output_address_ranges(f)
2790    self.output_footer(f)
2791    return
2792
2793  def output_address_ranges(self, f):
2794    regions = {}
2795    def print_region(_reader, start, size, _location):
2796      regions[start] = size
2797    self.reader.ForEachMemoryRegion(print_region)
2798    f.write("<h3>Available memory regions</h3>")
2799    f.write('<div class="code">')
2800    f.write("<table class=\"regions\">")
2801    f.write("<thead><tr>")
2802    f.write("<th>Start address</th>")
2803    f.write("<th>End address</th>")
2804    f.write("<th>Number of bytes</th>")
2805    f.write("</tr></thead>")
2806    for start in sorted(regions):
2807      size = regions[start]
2808      f.write("<tr>")
2809      f.write("<td>%s</td>" % self.format_address(start))
2810      f.write("<td>&nbsp;%s</td>" % self.format_address(start + size))
2811      f.write("<td>&nbsp;%d</td>" % size)
2812      f.write("</tr>")
2813    f.write("</table>")
2814    f.write('</div>')
2815    return
2816
2817  def output_module_details(self, f, module):
2818    f.write("<b>%s</b>" % GetModuleName(self.reader, module))
2819    file_version = GetVersionString(module.version_info.dwFileVersionMS,
2820                                    module.version_info.dwFileVersionLS)
2821    product_version = GetVersionString(module.version_info.dwProductVersionMS,
2822                                       module.version_info.dwProductVersionLS)
2823    f.write("<br>&nbsp;&nbsp;")
2824    f.write("base: %s" % self.reader.FormatIntPtr(module.base_of_image))
2825    f.write("<br>&nbsp;&nbsp;")
2826    f.write("  end: %s" % self.reader.FormatIntPtr(module.base_of_image +
2827                                            module.size_of_image))
2828    f.write("<br>&nbsp;&nbsp;")
2829    f.write("  file version: %s" % file_version)
2830    f.write("<br>&nbsp;&nbsp;")
2831    f.write("  product version: %s" % product_version)
2832    f.write("<br>&nbsp;&nbsp;")
2833    time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
2834    f.write("  timestamp: %s" % time_date_stamp)
2835    f.write("<br>");
2836
2837  def output_modules(self, f):
2838    self.output_header(f)
2839    f.write('<div class="code">')
2840    for module in self.reader.module_list.modules:
2841      self.output_module_details(f, module)
2842    f.write("</div>")
2843    self.output_footer(f)
2844    return
2845
2846  def output_context(self, f, details):
2847    exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
2848    f.write("<h3>Exception context</h3>")
2849    f.write('<div class="code">')
2850    f.write("Thread id: %d" % exception_thread.id)
2851    f.write("&nbsp;&nbsp; Exception code: %08X<br/>" %
2852            self.reader.exception.exception.code)
2853    if details == InspectionWebFormatter.CONTEXT_FULL:
2854      if self.reader.exception.exception.parameter_count > 0:
2855        f.write("&nbsp;&nbsp; Exception parameters: ")
2856        for i in xrange(0, self.reader.exception.exception.parameter_count):
2857          f.write("%08x" % self.reader.exception.exception.information[i])
2858        f.write("<br><br>")
2859
2860    for r in CONTEXT_FOR_ARCH[self.reader.arch]:
2861      f.write(HTML_REG_FORMAT %
2862              (r, self.format_address(self.reader.Register(r))))
2863    # TODO(vitalyr): decode eflags.
2864    if self.reader.arch in [MD_CPU_ARCHITECTURE_ARM, MD_CPU_ARCHITECTURE_ARM64]:
2865      f.write("<b>cpsr</b>: %s" % bin(self.reader.exception_context.cpsr)[2:])
2866    else:
2867      f.write("<b>eflags</b>: %s" %
2868              bin(self.reader.exception_context.eflags)[2:])
2869    f.write('</div>')
2870    return
2871
2872  def align_down(self, a, size):
2873    alignment_correction = a % size
2874    return a - alignment_correction
2875
2876  def align_up(self, a, size):
2877    alignment_correction = (size - 1) - ((a + size - 1) % size)
2878    return a + alignment_correction
2879
2880  def format_object(self, address):
2881    heap_object = self.padawan.SenseObject(address)
2882    return cgi.escape(str(heap_object or ""))
2883
2884  def output_data(self, f, straddress, datakind):
2885    try:
2886      self.output_header(f)
2887      address = int(straddress, 0)
2888      if not self.reader.IsValidAddress(address):
2889        f.write("<h3>Address 0x%x not found in the dump.</h3>" % address)
2890        return
2891      region = self.reader.FindRegion(address)
2892      if datakind == "address":
2893        self.output_words(f, region[0], region[0] + region[1], address, "Dump")
2894      elif datakind == "ascii":
2895        self.output_ascii(f, region[0], region[0] + region[1], address)
2896      self.output_footer(f)
2897
2898    except ValueError:
2899      f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
2900    return
2901
2902  def output_words(self, f, start_address, end_address,
2903                   highlight_address, desc):
2904    region = self.reader.FindRegion(highlight_address)
2905    if region is None:
2906      f.write("<h3>Address 0x%x not found in the dump.</h3>" %
2907              (highlight_address))
2908      return
2909    size = self.heap.PointerSize()
2910    start_address = self.align_down(start_address, size)
2911    low = self.align_down(region[0], size)
2912    high = self.align_up(region[0] + region[1], size)
2913    if start_address < low:
2914      start_address = low
2915    end_address = self.align_up(end_address, size)
2916    if end_address > high:
2917      end_address = high
2918
2919    expand = ""
2920    if start_address != low or end_address != high:
2921      expand = ("(<a href=\"data.html?%s&amp;val=0x%x#highlight\">"
2922                " more..."
2923                " </a>)" %
2924                (self.encfilename, highlight_address))
2925
2926    f.write("<h3>%s 0x%x - 0x%x, "
2927            "highlighting <a href=\"#highlight\">0x%x</a> %s</h3>" %
2928            (desc, start_address, end_address, highlight_address, expand))
2929    f.write('<div class="code">')
2930    f.write("<table class=codedump>")
2931
2932    for j in xrange(0, end_address - start_address, size):
2933      slot = start_address + j
2934      heap_object = ""
2935      maybe_address = None
2936      end_region = region[0] + region[1]
2937      if slot < region[0] or slot + size > end_region:
2938        straddress = "0x"
2939        for i in xrange(end_region, slot + size):
2940          straddress += "??"
2941        for i in reversed(
2942            xrange(max(slot, region[0]), min(slot + size, end_region))):
2943          straddress += "%02x" % self.reader.ReadU8(i)
2944        for i in xrange(slot, region[0]):
2945          straddress += "??"
2946      else:
2947        maybe_address = self.reader.ReadUIntPtr(slot)
2948        straddress = self.format_address(maybe_address)
2949        if maybe_address:
2950          heap_object = self.format_object(maybe_address)
2951
2952      address_fmt = "%s&nbsp;</td>"
2953      if slot == highlight_address:
2954        f.write("<tr class=highlight-line>")
2955        address_fmt = "<a id=highlight></a>%s&nbsp;</td>"
2956      elif slot < highlight_address and highlight_address < slot + size:
2957        f.write("<tr class=inexact-highlight-line>")
2958        address_fmt = "<a id=highlight></a>%s&nbsp;</td>"
2959      else:
2960        f.write("<tr>")
2961
2962      f.write("<td>")
2963      self.output_comment_box(f, "da-", slot)
2964      f.write("</td>")
2965      self.td_from_address(f, slot)
2966      f.write(address_fmt % self.format_address(slot))
2967      self.td_from_address(f, maybe_address)
2968      f.write(":&nbsp;%s&nbsp;</td>" % straddress)
2969      f.write("<td>")
2970      if maybe_address != None:
2971        self.output_comment_box(
2972            f, "sv-" + self.reader.FormatIntPtr(slot), maybe_address)
2973      f.write("</td>")
2974      f.write("<td>%s</td>" % (heap_object or ''))
2975      f.write("</tr>")
2976    f.write("</table>")
2977    f.write("</div>")
2978    return
2979
2980  def output_ascii(self, f, start_address, end_address, highlight_address):
2981    region = self.reader.FindRegion(highlight_address)
2982    if region is None:
2983      f.write("<h3>Address %x not found in the dump.</h3>" %
2984          highlight_address)
2985      return
2986    if start_address < region[0]:
2987      start_address = region[0]
2988    if end_address > region[0] + region[1]:
2989      end_address = region[0] + region[1]
2990
2991    expand = ""
2992    if start_address != region[0] or end_address != region[0] + region[1]:
2993      link = ("data.html?%s&amp;val=0x%x&amp;type=ascii#highlight" %
2994              (self.encfilename, highlight_address))
2995      expand = "(<a href=\"%s\">more...</a>)" % link
2996
2997    f.write("<h3>ASCII dump 0x%x - 0x%x, highlighting 0x%x %s</h3>" %
2998            (start_address, end_address, highlight_address, expand))
2999
3000    line_width = 64
3001
3002    f.write('<div class="code">')
3003
3004    start = self.align_down(start_address, line_width)
3005
3006    for i in xrange(end_address - start):
3007      address = start + i
3008      if address % 64 == 0:
3009        if address != start:
3010          f.write("<br>")
3011        f.write("0x%08x:&nbsp;" % address)
3012      if address < start_address:
3013        f.write("&nbsp;")
3014      else:
3015        if address == highlight_address:
3016          f.write("<span class=\"highlight\">")
3017        code = self.reader.ReadU8(address)
3018        if code < 127 and code >= 32:
3019          f.write("&#")
3020          f.write(str(code))
3021          f.write(";")
3022        else:
3023          f.write("&middot;")
3024        if address == highlight_address:
3025          f.write("</span>")
3026    f.write("</div>")
3027    return
3028
3029  def output_disasm(self, f, straddress, strexact):
3030    try:
3031      self.output_header(f)
3032      address = int(straddress, 0)
3033      if not self.reader.IsValidAddress(address):
3034        f.write("<h3>Address 0x%x not found in the dump.</h3>" % address)
3035        return
3036      region = self.reader.FindRegion(address)
3037      self.output_disasm_range(
3038          f, region[0], region[0] + region[1], address, strexact == "on")
3039      self.output_footer(f)
3040    except ValueError:
3041      f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
3042    return
3043
3044  def output_disasm_range(
3045      self, f, start_address, end_address, highlight_address, exact):
3046    region = self.reader.FindRegion(highlight_address)
3047    if start_address < region[0]:
3048      start_address = region[0]
3049    if end_address > region[0] + region[1]:
3050      end_address = region[0] + region[1]
3051    count = end_address - start_address
3052    lines = self.reader.GetDisasmLines(start_address, count)
3053    found = False
3054    if exact:
3055      for line in lines:
3056        if line[0] + start_address == highlight_address:
3057          found = True
3058          break
3059      if not found:
3060        start_address = highlight_address
3061        count = end_address - start_address
3062        lines = self.reader.GetDisasmLines(highlight_address, count)
3063    expand = ""
3064    if start_address != region[0] or end_address != region[0] + region[1]:
3065      exactness = ""
3066      if exact and not found and end_address == region[0] + region[1]:
3067        exactness = "&amp;exact=off"
3068      expand = ("(<a href=\"disasm.html?%s%s"
3069                "&amp;val=0x%x#highlight\">more...</a>)" %
3070                (self.encfilename, exactness, highlight_address))
3071
3072    f.write("<h3>Disassembling 0x%x - 0x%x, highlighting 0x%x %s</h3>" %
3073            (start_address, end_address, highlight_address, expand))
3074    f.write('<div class="code">')
3075    f.write("<table class=\"codedump\">");
3076    for i in xrange(len(lines)):
3077      line = lines[i]
3078      next_address = count
3079      if i + 1 < len(lines):
3080        next_line = lines[i + 1]
3081        next_address = next_line[0]
3082      self.format_disasm_line(
3083          f, start_address, line, next_address, highlight_address)
3084    f.write("</table>")
3085    f.write("</div>")
3086    return
3087
3088  def annotate_disasm_addresses(self, line):
3089    extra = []
3090    for m in ADDRESS_RE.finditer(line):
3091      maybe_address = int(m.group(0), 16)
3092      formatted_address = self.format_address(maybe_address, m.group(0))
3093      line = line.replace(m.group(0), formatted_address)
3094      object_info = self.padawan.SenseObject(maybe_address)
3095      if not object_info:
3096        continue
3097      extra.append(cgi.escape(str(object_info)))
3098    if len(extra) == 0:
3099      return line
3100    return ("%s <span class=disasmcomment>;; %s</span>" %
3101            (line, ", ".join(extra)))
3102
3103  def format_disasm_line(
3104      self, f, start, line, next_address, highlight_address):
3105    line_address = start + line[0]
3106    address_fmt = "  <td>%s</td>"
3107    if line_address == highlight_address:
3108      f.write("<tr class=highlight-line>")
3109      address_fmt = "  <td><a id=highlight>%s</a></td>"
3110    elif (line_address < highlight_address and
3111          highlight_address < next_address + start):
3112      f.write("<tr class=inexact-highlight-line>")
3113      address_fmt = "  <td><a id=highlight>%s</a></td>"
3114    else:
3115      f.write("<tr>")
3116    num_bytes = next_address - line[0]
3117    stack_slot = self.heap.stack_map.get(line_address)
3118    marker = ""
3119    if stack_slot:
3120      marker = "=>"
3121
3122    code = line[1]
3123
3124    # Some disassemblers insert spaces between each byte,
3125    # while some do not.
3126    if code[2] == " ":
3127        op_offset = 3 * num_bytes - 1
3128    else:
3129        op_offset = 2 * num_bytes
3130
3131    # Compute the actual call target which the disassembler is too stupid
3132    # to figure out (it adds the call offset to the disassembly offset rather
3133    # than the absolute instruction address).
3134    if self.heap.reader.arch == MD_CPU_ARCHITECTURE_X86:
3135      if code.startswith("e8"):
3136        words = code.split()
3137        if len(words) > 6 and words[5] == "call":
3138          offset = int(words[4] + words[3] + words[2] + words[1], 16)
3139          target = (line_address + offset + 5) & 0xFFFFFFFF
3140          code = code.replace(words[6], "0x%08x" % target)
3141    # TODO(jkummerow): port this hack to ARM and x64.
3142
3143    opcodes = code[:op_offset]
3144    code = self.annotate_disasm_addresses(code[op_offset:])
3145    f.write("  <td>")
3146    self.output_comment_box(f, "codel-", line_address)
3147    f.write("</td>")
3148    f.write(address_fmt % marker)
3149    f.write("  ")
3150    self.td_from_address(f, line_address)
3151    f.write(self.format_address(line_address))
3152    f.write(" (+0x%x)</td>" % line[0])
3153    f.write("<td>:&nbsp;%s&nbsp;</td>" % opcodes)
3154    f.write("<td>%s</td>" % code)
3155    f.write("</tr>")
3156
3157  def output_comment_box(self, f, prefix, address):
3158    comment = self.comments.get_comment(address)
3159    value = ""
3160    if comment:
3161      value = " value=\"%s\"" % cgi.escape(comment)
3162    f.write("<input type=text class=ci "
3163            "id=%s-address-0x%s onchange=c()%s>" %
3164            (prefix,
3165             self.reader.FormatIntPtr(address),
3166             value))
3167
3168  MAX_FOUND_RESULTS = 100
3169
3170  def output_find_results(self, f, results):
3171    f.write("Addresses")
3172    toomany = len(results) > self.MAX_FOUND_RESULTS
3173    if toomany:
3174      f.write("(found %i results, displaying only first %i)" %
3175              (len(results), self.MAX_FOUND_RESULTS))
3176    f.write(": ")
3177    results = sorted(results)
3178    results = results[:min(len(results), self.MAX_FOUND_RESULTS)]
3179    for address in results:
3180      f.write("<span %s>%s</span>" %
3181              (self.comments.get_style_class_string(address),
3182               self.format_address(address)))
3183    if toomany:
3184      f.write("...")
3185
3186
3187  def output_page_info(self, f, page_kind, page_address, my_page_address):
3188    if my_page_address == page_address and page_address != 0:
3189      f.write("Marked first %s page." % page_kind)
3190    else:
3191      f.write("<span id=\"%spage\" style=\"display:none\">" % page_kind)
3192      f.write("Marked first %s page." % page_kind)
3193      f.write("</span>\n")
3194      f.write("<button onclick=\"onpage('%spage', '0x%x')\">" %
3195              (page_kind, my_page_address))
3196      f.write("Mark as first %s page</button>" % page_kind)
3197    return
3198
3199  def output_search_res(self, f, straddress):
3200    try:
3201      self.output_header(f)
3202      f.write("<h3>Search results for %s</h3>" % straddress)
3203
3204      address = int(straddress, 0)
3205
3206      f.write("Comment: ")
3207      self.output_comment_box(f, "search-", address)
3208      f.write("<br>")
3209
3210      page_address = address & ~self.heap.PageAlignmentMask()
3211
3212      f.write("Page info: ")
3213      self.output_page_info(f, "old", self.padawan.known_first_old_page, \
3214                            page_address)
3215      self.output_page_info(f, "map", self.padawan.known_first_map_page, \
3216                            page_address)
3217
3218      if not self.reader.IsValidAddress(address):
3219        f.write("<h3>The contents at address %s not found in the dump.</h3>" % \
3220                straddress)
3221      else:
3222        # Print as words
3223        self.output_words(f, address - 8, address + 32, address, "Dump")
3224
3225        # Print as ASCII
3226        f.write("<hr>")
3227        self.output_ascii(f, address, address + 256, address)
3228
3229        # Print as code
3230        f.write("<hr>")
3231        self.output_disasm_range(f, address - 16, address + 16, address, True)
3232
3233      aligned_res, unaligned_res = self.reader.FindWordList(address)
3234
3235      if len(aligned_res) > 0:
3236        f.write("<h3>Occurrences of 0x%x at aligned addresses</h3>" %
3237                address)
3238        self.output_find_results(f, aligned_res)
3239
3240      if len(unaligned_res) > 0:
3241        f.write("<h3>Occurrences of 0x%x at unaligned addresses</h3>" % \
3242                address)
3243        self.output_find_results(f, unaligned_res)
3244
3245      if len(aligned_res) + len(unaligned_res) == 0:
3246        f.write("<h3>No occurrences of 0x%x found in the dump</h3>" % address)
3247
3248      self.output_footer(f)
3249
3250    except ValueError:
3251      f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
3252    return
3253
3254  def output_disasm_pc(self, f):
3255    address = self.reader.ExceptionIP()
3256    if not self.reader.IsValidAddress(address):
3257      return
3258    self.output_disasm_range(f, address - 16, address + 16, address, True)
3259
3260
3261WEB_DUMPS_HEADER = """
3262<!DOCTYPE html>
3263<html>
3264<head>
3265<meta content="text/html; charset=utf-8" http-equiv="content-type">
3266<style media="screen" type="text/css">
3267
3268.dumplist {
3269  border-collapse : collapse;
3270  border-spacing : 0px;
3271  font-family: monospace;
3272}
3273
3274.dumpcomments {
3275  border : 1px solid LightGray;
3276  width : 32em;
3277}
3278
3279</style>
3280
3281<script type="application/javascript">
3282
3283var dump_str = "dump-";
3284var dump_len = dump_str.length;
3285
3286function dump_comment() {
3287  var s = event.srcElement.id;
3288  var index = s.indexOf(dump_str);
3289  if (index >= 0) {
3290    send_dump_desc(s.substring(index + dump_len), event.srcElement.value);
3291  }
3292}
3293
3294function send_dump_desc(name, desc) {
3295  xmlhttp = new XMLHttpRequest();
3296  name = encodeURIComponent(name)
3297  desc = encodeURIComponent(desc)
3298  xmlhttp.open("GET",
3299      "setdumpdesc?dump=" + name +
3300      "&description=" + desc, true);
3301  xmlhttp.send();
3302}
3303
3304</script>
3305
3306<title>Dump list</title>
3307</head>
3308
3309<body>
3310"""
3311
3312WEB_DUMPS_FOOTER = """
3313</body>
3314</html>
3315"""
3316
3317DUMP_FILE_RE = re.compile(r"[-_0-9a-zA-Z][-\._0-9a-zA-Z]*\.dmp$")
3318
3319
3320class InspectionWebServer(BaseHTTPServer.HTTPServer):
3321  def __init__(self, port_number, switches, minidump_name):
3322    BaseHTTPServer.HTTPServer.__init__(
3323        self, ('localhost', port_number), InspectionWebHandler)
3324    splitpath = os.path.split(minidump_name)
3325    self.dumppath = splitpath[0]
3326    self.dumpfilename = splitpath[1]
3327    self.default_formatter = InspectionWebFormatter(
3328        switches, minidump_name, self)
3329    self.formatters = { self.dumpfilename : self.default_formatter }
3330    self.switches = switches
3331
3332  def output_dump_desc_field(self, f, name):
3333    try:
3334      descfile = open(os.path.join(self.dumppath, name + ".desc"), "r")
3335      desc = descfile.readline()
3336      descfile.close()
3337    except IOError:
3338      desc = ""
3339    f.write("<input type=\"text\" class=\"dumpcomments\" "
3340            "id=\"dump-%s\" onchange=\"dump_comment()\" value=\"%s\">\n" %
3341            (cgi.escape(name), desc))
3342
3343  def set_dump_desc(self, name, description):
3344    if not DUMP_FILE_RE.match(name):
3345      return False
3346    fname = os.path.join(self.dumppath, name)
3347    if not os.path.isfile(fname):
3348      return False
3349    fname = fname + ".desc"
3350    descfile = open(fname, "w")
3351    descfile.write(description)
3352    descfile.close()
3353    return True
3354
3355  def get_dump_formatter(self, name):
3356    if name is None:
3357      return self.default_formatter
3358    else:
3359      if not DUMP_FILE_RE.match(name):
3360        raise WebParameterError("Invalid name '%s'" % name)
3361      formatter = self.formatters.get(name, None)
3362      if formatter is None:
3363        try:
3364          formatter = InspectionWebFormatter(
3365              self.switches, os.path.join(self.dumppath, name), self)
3366          self.formatters[name] = formatter
3367        except IOError:
3368          raise WebParameterError("Could not open dump '%s'" % name)
3369      return formatter
3370
3371  def output_dumps(self, f):
3372    f.write(WEB_DUMPS_HEADER)
3373    f.write("<h3>List of available dumps</h3>")
3374    f.write("<table class=\"dumplist\">\n")
3375    f.write("<thead><tr>")
3376    f.write("<th>Name</th>")
3377    f.write("<th>File time</th>")
3378    f.write("<th>Comment</th>")
3379    f.write("</tr></thead>")
3380    dumps_by_time = {}
3381    for fname in os.listdir(self.dumppath):
3382      if DUMP_FILE_RE.match(fname):
3383        mtime = os.stat(os.path.join(self.dumppath, fname)).st_mtime
3384        fnames = dumps_by_time.get(mtime, [])
3385        fnames.append(fname)
3386        dumps_by_time[mtime] = fnames
3387
3388    for mtime in sorted(dumps_by_time, reverse=True):
3389      fnames = dumps_by_time[mtime]
3390      for fname in fnames:
3391        f.write("<tr>\n")
3392        f.write("<td><a href=\"summary.html?%s\">%s</a></td>\n" % (
3393            (urllib.urlencode({ 'dump' : fname }), fname)))
3394        f.write("<td>&nbsp;&nbsp;&nbsp;")
3395        f.write(datetime.datetime.fromtimestamp(mtime))
3396        f.write("</td>")
3397        f.write("<td>&nbsp;&nbsp;&nbsp;")
3398        self.output_dump_desc_field(f, fname)
3399        f.write("</td>")
3400        f.write("</tr>\n")
3401    f.write("</table>\n")
3402    f.write(WEB_DUMPS_FOOTER)
3403    return
3404
3405class InspectionShell(cmd.Cmd):
3406  def __init__(self, reader, heap):
3407    cmd.Cmd.__init__(self)
3408    self.reader = reader
3409    self.heap = heap
3410    self.padawan = InspectionPadawan(reader, heap)
3411    self.prompt = "(grok) "
3412
3413    self.dd_start = 0
3414    self.dd_num = 0x10
3415    self.u_start = 0
3416    self.u_num = 0
3417
3418  def EvalExpression(self, expr):
3419    # Auto convert hex numbers to a python compatible format
3420    if expr[:2] == "00":
3421      expr = "0x"+expr
3422    result = None
3423    try:
3424      # Ugly hack to patch in register values.
3425      registers = [register
3426                   for register,value in self.reader.ContextDescriptor().fields]
3427      registers.sort(key=lambda r: len(r))
3428      registers.reverse()
3429      for register in registers:
3430        expr = expr.replace("$"+register, str(self.reader.Register(register)))
3431      result = eval(expr)
3432    except Exception as e:
3433      print("**** Could not evaluate '%s': %s" % (expr, e))
3434      raise e
3435    return result
3436
3437  def ParseAddressExpr(self, expr):
3438    address = 0;
3439    try:
3440      result = self.EvalExpression(expr)
3441    except:
3442      return 0
3443    try:
3444      address = int(result)
3445    except Exception as e:
3446      print("**** Could not convert '%s' => %s to valid address: %s" % (
3447          expr, result , e))
3448    return address
3449
3450  def do_help(self, cmd=None):
3451    if len(cmd) == 0:
3452      print "Available commands"
3453      print "=" * 79
3454      prefix = "do_"
3455      methods = inspect.getmembers(InspectionShell, predicate=inspect.ismethod)
3456      for name,method in methods:
3457        if not name.startswith(prefix): continue
3458        doc = inspect.getdoc(method)
3459        if not doc: continue
3460        name = prefix.join(name.split(prefix)[1:])
3461        description = doc.splitlines()[0]
3462        print (name + ": ").ljust(16) + description
3463      print "=" * 79
3464    else:
3465      return super(InspectionShell, self).do_help(cmd)
3466
3467  def do_p(self, cmd):
3468    """ see print """
3469    return self.do_print(cmd)
3470
3471  def do_print(self, cmd):
3472    """
3473    Evaluate an arbitrary python command.
3474    """
3475    try:
3476      print(self.EvalExpression(cmd))
3477    except:
3478      pass
3479
3480  def do_da(self, address):
3481    """ see display_ascii"""
3482    return self.do_display_ascii(address)
3483
3484  def do_display_ascii(self, address):
3485    """
3486     Print ASCII string starting at specified address.
3487    """
3488    address = self.ParseAddressExpr(address)
3489    string = self.reader.ReadAsciiString(address)
3490    if string == "":
3491      print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address)
3492    else:
3493      print "%s\n" % string
3494
3495  def do_dsa(self, address):
3496    """ see display_stack_ascii"""
3497    return self.do_display_stack_ascii(address)
3498
3499  def do_display_stack_ascii(self, address):
3500    """
3501    Print ASCII stack error message.
3502    """
3503    if self.reader.exception is None:
3504      print "Minidump has no exception info"
3505      return
3506    if len(address) == 0:
3507      address = None
3508    else:
3509      address = self.ParseAddressExpr(address)
3510    self.padawan.PrintStackTraceMessage(address)
3511
3512  def do_dd(self, args):
3513    """
3514     Interpret memory in the given region [address, address + num * word_size)
3515
3516     (if available) as a sequence of words. Automatic alignment is not performed.
3517     If the num is not specified, a default value of 16 words is usif not self.Is
3518     If no address is given, dd continues printing at the next word.
3519
3520     Synopsis: dd 0x<address>|$register [0x<num>]
3521    """
3522    if len(args) != 0:
3523      args = args.split(' ')
3524      self.dd_start = self.ParseAddressExpr(args[0])
3525      self.dd_num = int(args[1], 16) if len(args) > 1 else 0x10
3526    else:
3527      self.dd_start += self.dd_num * self.reader.PointerSize()
3528    if not self.reader.IsAlignedAddress(self.dd_start):
3529      print "Warning: Dumping un-aligned memory, is this what you had in mind?"
3530    end = self.dd_start + self.reader.PointerSize() * self.dd_num
3531    self.padawan.InterpretMemory(self.dd_start, end)
3532
3533  def do_do(self, address):
3534    """ see display_object """
3535    return self.do_display_object(address)
3536
3537  def do_display_object(self, address):
3538    """
3539     Interpret memory at the given address as a V8 object.
3540
3541     Automatic alignment makes sure that you can pass tagged as well as
3542     un-tagged addresses.
3543    """
3544    address = self.ParseAddressExpr(address)
3545    if self.reader.IsAlignedAddress(address):
3546      address = address + 1
3547    elif not self.heap.IsTaggedObjectAddress(address):
3548      print "Address doesn't look like a valid pointer!"
3549      return
3550    heap_object = self.padawan.SenseObject(address)
3551    if heap_object:
3552      heap_object.Print(Printer())
3553    else:
3554      print "Address cannot be interpreted as object!"
3555
3556  def do_dso(self, args):
3557    """ see display_stack_objects """
3558    return self.do_display_stack_objects(args)
3559
3560  def do_display_stack_objects(self, args):
3561    """
3562    Find and Print object pointers in the given range.
3563
3564    Print all possible object pointers that are on the stack or in the given
3565    address range.
3566
3567    Usage: dso [START_ADDR,[END_ADDR]]
3568    """
3569    start = self.reader.StackTop()
3570    end = self.reader.StackBottom()
3571    if len(args) != 0:
3572      args = args.split(' ')
3573      start = self.ParseAddressExpr(args[0])
3574      end = self.ParseAddressExpr(args[1]) if len(args) > 1 else end
3575    objects = self.heap.FindObjectPointers(start, end)
3576    for address in objects:
3577      heap_object = self.padawan.SenseObject(address)
3578      info = ""
3579      if heap_object:
3580        info = str(heap_object)
3581      print("%s %s" % (self.padawan.FormatIntPtr(address), info))
3582
3583  def do_do_desc(self, address):
3584    """
3585      Print a descriptor array in a readable format.
3586    """
3587    start = self.ParseAddressExpr(address)
3588    if ((start & 1) == 1): start = start - 1
3589    DescriptorArray(FixedArray(self.heap, None, start)).Print(Printer())
3590
3591  def do_do_map(self, address):
3592    """
3593      Print a Map in a readable format.
3594    """
3595    start = self.ParseAddressExpr(address)
3596    if ((start & 1) == 1): start = start - 1
3597    Map(self.heap, None, start).Print(Printer())
3598
3599  def do_do_trans(self, address):
3600    """
3601      Print a transition array in a readable format.
3602    """
3603    start = self.ParseAddressExpr(address)
3604    if ((start & 1) == 1): start = start - 1
3605    TransitionArray(FixedArray(self.heap, None, start)).Print(Printer())
3606
3607  def do_dp(self, address):
3608    """ see display_page """
3609    return self.do_display_page(address)
3610
3611  def do_display_page(self, address):
3612    """
3613     Prints details about the V8 heap page of the given address.
3614
3615     Interpret memory at the given address as being on a V8 heap page
3616     and print information about the page header (if available).
3617    """
3618    address = self.ParseAddressExpr(address)
3619    page_address = address & ~self.heap.PageAlignmentMask()
3620    if self.reader.IsValidAddress(page_address):
3621      print "**** Not Implemented"
3622      return
3623    else:
3624      print "Page header is not available!"
3625
3626  def do_k(self, arguments):
3627    """
3628     Teach V8 heap layout information to the inspector.
3629
3630     This increases the amount of annotations the inspector can produce while
3631     dumping data. The first page of each heap space is of particular interest
3632     because it contains known objects that do not move.
3633    """
3634    self.padawan.PrintKnowledge()
3635
3636  def do_ko(self, address):
3637    """ see known_oldspace """
3638    return self.do_known_oldspace(address)
3639
3640  def do_known_oldspace(self, address):
3641    """
3642     Teach V8 heap layout information to the inspector.
3643
3644     Set the first old space page by passing any pointer into that page.
3645    """
3646    address = self.ParseAddressExpr(address)
3647    page_address = address & ~self.heap.PageAlignmentMask()
3648    self.padawan.known_first_old_page = page_address
3649
3650  def do_km(self, address):
3651    """ see known_map """
3652    return self.do_known_map(address)
3653
3654  def do_known_map(self, address):
3655    """
3656     Teach V8 heap layout information to the inspector.
3657
3658     Set the first map-space page by passing any pointer into that page.
3659    """
3660    address = self.ParseAddressExpr(address)
3661    page_address = address & ~self.heap.PageAlignmentMask()
3662    self.padawan.known_first_map_page = page_address
3663
3664  def do_list(self, smth):
3665    """
3666     List all available memory regions.
3667    """
3668    def print_region(reader, start, size, location):
3669      print "  %s - %s (%d bytes)" % (reader.FormatIntPtr(start),
3670                                      reader.FormatIntPtr(start + size),
3671                                      size)
3672    print "Available memory regions:"
3673    self.reader.ForEachMemoryRegion(print_region)
3674
3675  def do_lm(self, arg):
3676    """ see list_modules """
3677    return self.do_list_modules(arg)
3678
3679  def do_list_modules(self, arg):
3680    """
3681     List details for all loaded modules in the minidump.
3682
3683     An argument can be passed to limit the output to only those modules that
3684     contain the argument as a substring (case insensitive match).
3685    """
3686    for module in self.reader.module_list.modules:
3687      if arg:
3688        name = GetModuleName(self.reader, module).lower()
3689        if name.find(arg.lower()) >= 0:
3690          PrintModuleDetails(self.reader, module)
3691      else:
3692        PrintModuleDetails(self.reader, module)
3693    print
3694
3695  def do_s(self, word):
3696    """ see search """
3697    return self.do_search(word)
3698
3699  def do_search(self, word):
3700    """
3701     Search for a given word in available memory regions.
3702
3703     The given word is expanded to full pointer size and searched at aligned
3704     as well as un-aligned memory locations. Use 'sa' to search aligned locations
3705     only.
3706    """
3707    try:
3708      word = self.ParseAddressExpr(word)
3709    except ValueError:
3710      print "Malformed word, prefix with '0x' to use hexadecimal format."
3711      return
3712    print "Searching for word %d/0x%s:" % (word, self.reader.FormatIntPtr(word))
3713    self.reader.FindWord(word)
3714
3715  def do_sh(self, none):
3716    """
3717     Search for the V8 Heap object in all available memory regions.
3718
3719     You might get lucky and find this rare treasure full of invaluable
3720     information.
3721    """
3722    print "**** Not Implemented"
3723
3724  def do_u(self, args):
3725    """ see disassemble """
3726    return self.do_disassemble(args)
3727
3728  def do_disassemble(self, args):
3729    """
3730     Unassemble memory in the region [address, address + size).
3731
3732     If the size is not specified, a default value of 32 bytes is used.
3733     Synopsis: u 0x<address> 0x<size>
3734    """
3735    if len(args) != 0:
3736      args = args.split(' ')
3737      self.u_start = self.ParseAddressExpr(args[0])
3738      self.u_size = self.ParseAddressExpr(args[1]) if len(args) > 1 else 0x20
3739      skip = False
3740    else:
3741      # Skip the first instruction if we reuse the last address.
3742      skip = True
3743
3744    if not self.reader.IsValidAddress(self.u_start):
3745      print "Address %s is not contained within the minidump!" % (
3746          self.reader.FormatIntPtr(self.u_start))
3747      return
3748    lines = self.reader.GetDisasmLines(self.u_start, self.u_size)
3749    if len(lines) == 0:
3750      print "Address %s could not be disassembled!" % (
3751          self.reader.FormatIntPtr(self.u_start))
3752      print "    Could not disassemble using %s." % OBJDUMP_BIN
3753      print "    Pass path to architecture specific objdump via --objdump?"
3754      return
3755    for line in lines:
3756      if skip:
3757        skip = False
3758        continue
3759      print FormatDisasmLine(self.u_start, self.heap, line)
3760    # Set the next start address = last line
3761    self.u_start += lines[-1][0]
3762    print
3763
3764  def do_EOF(self, none):
3765    raise KeyboardInterrupt
3766
3767EIP_PROXIMITY = 64
3768
3769CONTEXT_FOR_ARCH = {
3770    MD_CPU_ARCHITECTURE_AMD64:
3771      ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip',
3772       'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'],
3773    MD_CPU_ARCHITECTURE_ARM:
3774      ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
3775       'r10', 'r11', 'r12', 'sp', 'lr', 'pc'],
3776    MD_CPU_ARCHITECTURE_ARM64:
3777      ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
3778       'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 'r16', 'r17', 'r18', 'r19',
3779       'r20', 'r21', 'r22', 'r23', 'r24', 'r25', 'r26', 'r27', 'r28',
3780       'fp', 'lr', 'sp', 'pc'],
3781    MD_CPU_ARCHITECTURE_X86:
3782      ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
3783}
3784
3785KNOWN_MODULES = {'chrome.exe', 'chrome.dll'}
3786
3787def GetVersionString(ms, ls):
3788  return "%d.%d.%d.%d" % (ms >> 16, ms & 0xffff, ls >> 16, ls & 0xffff)
3789
3790
3791def GetModuleName(reader, module):
3792  name = reader.ReadMinidumpString(module.module_name_rva)
3793  # simplify for path manipulation
3794  name = name.encode('utf-8')
3795  return str(os.path.basename(str(name).replace("\\", "/")))
3796
3797
3798def PrintModuleDetails(reader, module):
3799  print "%s" % GetModuleName(reader, module)
3800  file_version = GetVersionString(module.version_info.dwFileVersionMS,
3801                                  module.version_info.dwFileVersionLS);
3802  product_version = GetVersionString(module.version_info.dwProductVersionMS,
3803                                     module.version_info.dwProductVersionLS)
3804  print "  base: %s" % reader.FormatIntPtr(module.base_of_image)
3805  print "  end: %s" % reader.FormatIntPtr(module.base_of_image +
3806                                          module.size_of_image)
3807  print "  file version: %s" % file_version
3808  print "  product version: %s" % product_version
3809  time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
3810  print "  timestamp: %s" % time_date_stamp
3811
3812
3813def AnalyzeMinidump(options, minidump_name):
3814  reader = MinidumpReader(options, minidump_name)
3815  heap = None
3816
3817  stack_top = reader.ExceptionSP()
3818  stack_bottom = reader.StackBottom()
3819  stack_map = {reader.ExceptionIP(): -1}
3820  for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
3821    maybe_address = reader.ReadUIntPtr(slot)
3822    if not maybe_address in stack_map:
3823      stack_map[maybe_address] = slot
3824
3825  heap = V8Heap(reader, stack_map)
3826  padawan = InspectionPadawan(reader, heap)
3827
3828  DebugPrint("========================================")
3829  if reader.exception is None:
3830    print "Minidump has no exception info"
3831  else:
3832    print "Address markers:"
3833    print "  T = valid tagged pointer in the minidump"
3834    print "  S = address on the exception stack"
3835    print "  C = address in loaded C/C++ module"
3836    print "  * = address in the minidump"
3837    print ""
3838    print "Exception info:"
3839    exception_thread = reader.ExceptionThread()
3840    print "  thread id: %d" % exception_thread.id
3841    print "  code:      %08X" % reader.exception.exception.code
3842    print "  context:"
3843    context = CONTEXT_FOR_ARCH[reader.arch]
3844    maxWidth = max(map(lambda s: len(s), context))
3845    for r in context:
3846      register_value = reader.Register(r)
3847      print "    %s: %s" % (r.rjust(maxWidth),
3848                            heap.FormatIntPtr(register_value))
3849    # TODO(vitalyr): decode eflags.
3850    if reader.arch in [MD_CPU_ARCHITECTURE_ARM, MD_CPU_ARCHITECTURE_ARM64]:
3851      print "    cpsr: %s" % bin(reader.exception_context.cpsr)[2:]
3852    else:
3853      print "    eflags: %s" % bin(reader.exception_context.eflags)[2:]
3854
3855    print
3856    print "  modules:"
3857    for module in reader.module_list.modules:
3858      name = GetModuleName(reader, module)
3859      if name in KNOWN_MODULES:
3860        print "    %s at %08X" % (name, module.base_of_image)
3861        reader.TryLoadSymbolsFor(name, module)
3862    print
3863
3864    print "  stack-top:    %s" % heap.FormatIntPtr(reader.StackTop())
3865    print "  stack-bottom: %s" % heap.FormatIntPtr(reader.StackBottom())
3866    print ""
3867
3868    if options.shell:
3869      padawan.PrintStackTraceMessage(print_message=False)
3870
3871    print "Disassembly around exception.eip:"
3872    eip_symbol = reader.FindSymbol(reader.ExceptionIP())
3873    if eip_symbol is not None:
3874      print eip_symbol
3875    disasm_start = reader.ExceptionIP() - EIP_PROXIMITY
3876    disasm_bytes = 2 * EIP_PROXIMITY
3877    if (options.full):
3878      full_range = reader.FindRegion(reader.ExceptionIP())
3879      if full_range is not None:
3880        disasm_start = full_range[0]
3881        disasm_bytes = full_range[1]
3882
3883    lines = reader.GetDisasmLines(disasm_start, disasm_bytes)
3884
3885    if not lines:
3886      print "Could not disassemble using %s." % OBJDUMP_BIN
3887      print "Pass path to architecture specific objdump via --objdump?"
3888
3889    for line in lines:
3890      print FormatDisasmLine(disasm_start, heap, line)
3891    print
3892
3893  if heap is None:
3894    heap = V8Heap(reader, None)
3895
3896  if options.full:
3897    FullDump(reader, heap)
3898
3899  if options.command:
3900    InspectionShell(reader, heap).onecmd(options.command)
3901
3902  if options.shell:
3903    try:
3904      InspectionShell(reader, heap).cmdloop("type help to get help")
3905    except KeyboardInterrupt:
3906      print "Kthxbye."
3907  elif not options.command:
3908    if reader.exception is not None:
3909      print "Annotated stack (from exception.esp to bottom):"
3910      stack_start = padawan.PrintStackTraceMessage()
3911      padawan.InterpretMemory(stack_start, stack_bottom)
3912  reader.Dispose()
3913
3914
3915if __name__ == "__main__":
3916  parser = optparse.OptionParser(USAGE)
3917  parser.add_option("-s", "--shell", dest="shell", action="store_true",
3918                    help="start an interactive inspector shell")
3919  parser.add_option("-w", "--web", dest="web", action="store_true",
3920                    help="start a web server on localhost:%i" % PORT_NUMBER)
3921  parser.add_option("-c", "--command", dest="command", default="",
3922                    help="run an interactive inspector shell command and exit")
3923  parser.add_option("-f", "--full", dest="full", action="store_true",
3924                    help="dump all information contained in the minidump")
3925  parser.add_option("--symdir", dest="symdir", default=".",
3926                    help="directory containing *.pdb.sym file with symbols")
3927  parser.add_option("--objdump", default="",
3928                    help="objdump tool to use [default: %s]" % (
3929                        DEFAULT_OBJDUMP_BIN))
3930  options, args = parser.parse_args()
3931  if len(args) != 1:
3932    parser.print_help()
3933    sys.exit(1)
3934  if options.web:
3935    try:
3936      server = InspectionWebServer(PORT_NUMBER, options, args[0])
3937      print 'Started httpserver on port ' , PORT_NUMBER
3938      webbrowser.open('http://localhost:%i/summary.html' % PORT_NUMBER)
3939      server.serve_forever()
3940    except KeyboardInterrupt:
3941      print '^C received, shutting down the web server'
3942      server.socket.close()
3943  else:
3944    AnalyzeMinidump(options, args[0])
3945