The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / v8 / tools / grokdump.py @ f230a1cf

History | View | Annotate | Download (68.3 KB)

1
#!/usr/bin/env python
2
#
3
# Copyright 2012 the V8 project authors. All rights reserved.
4
# Redistribution and use in source and binary forms, with or without
5
# modification, are permitted provided that the following conditions are
6
# met:
7
#
8
#     * Redistributions of source code must retain the above copyright
9
#       notice, this list of conditions and the following disclaimer.
10
#     * Redistributions in binary form must reproduce the above
11
#       copyright notice, this list of conditions and the following
12
#       disclaimer in the documentation and/or other materials provided
13
#       with the distribution.
14
#     * Neither the name of Google Inc. nor the names of its
15
#       contributors may be used to endorse or promote products derived
16
#       from this software without specific prior written permission.
17
#
18
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29

    
30
import bisect
31
import cmd
32
import codecs
33
import ctypes
34
import datetime
35
import disasm
36
import mmap
37
import optparse
38
import os
39
import re
40
import struct
41
import sys
42
import types
43
import v8heapconst
44

    
45
USAGE="""usage: %prog [OPTIONS] [DUMP-FILE]
46

47
Minidump analyzer.
48

49
Shows the processor state at the point of exception including the
50
stack of the active thread and the referenced objects in the V8
51
heap. Code objects are disassembled and the addresses linked from the
52
stack (e.g. pushed return addresses) are marked with "=>".
53

54
Examples:
55
  $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp"""
56

    
57

    
58
DEBUG=False
59

    
60

    
61
def DebugPrint(s):
62
  if not DEBUG: return
63
  print s
64

    
65

    
66
class Descriptor(object):
67
  """Descriptor of a structure in a memory."""
68

    
69
  def __init__(self, fields):
70
    self.fields = fields
71
    self.is_flexible = False
72
    for _, type_or_func in fields:
73
      if isinstance(type_or_func, types.FunctionType):
74
        self.is_flexible = True
75
        break
76
    if not self.is_flexible:
77
      self.ctype = Descriptor._GetCtype(fields)
78
      self.size = ctypes.sizeof(self.ctype)
79

    
80
  def Read(self, memory, offset):
81
    if self.is_flexible:
82
      fields_copy = self.fields[:]
83
      last = 0
84
      for name, type_or_func in fields_copy:
85
        if isinstance(type_or_func, types.FunctionType):
86
          partial_ctype = Descriptor._GetCtype(fields_copy[:last])
87
          partial_object = partial_ctype.from_buffer(memory, offset)
88
          type = type_or_func(partial_object)
89
          if type is not None:
90
            fields_copy[last] = (name, type)
91
            last += 1
92
        else:
93
          last += 1
94
      complete_ctype = Descriptor._GetCtype(fields_copy[:last])
95
    else:
96
      complete_ctype = self.ctype
97
    return complete_ctype.from_buffer(memory, offset)
98

    
99
  @staticmethod
100
  def _GetCtype(fields):
101
    class Raw(ctypes.Structure):
102
      _fields_ = fields
103
      _pack_ = 1
104

    
105
      def __str__(self):
106
        return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field))
107
                               for field, _ in Raw._fields_) + "}"
108
    return Raw
109

    
110

    
111
def FullDump(reader, heap):
112
  """Dump all available memory regions."""
113
  def dump_region(reader, start, size, location):
114
    print
115
    while start & 3 != 0:
116
      start += 1
117
      size -= 1
118
      location += 1
119
    is_executable = reader.IsProbableExecutableRegion(location, size)
120
    is_ascii = reader.IsProbableASCIIRegion(location, size)
121

    
122
    if is_executable is not False:
123
      lines = reader.GetDisasmLines(start, size)
124
      for line in lines:
125
        print FormatDisasmLine(start, heap, line)
126
      print
127

    
128
    if is_ascii is not False:
129
      # Output in the same format as the Unix hd command
130
      addr = start
131
      for slot in xrange(location, location + size, 16):
132
        hex_line = ""
133
        asc_line = ""
134
        for i in xrange(0, 16):
135
          if slot + i < location + size:
136
            byte = ctypes.c_uint8.from_buffer(reader.minidump, slot + i).value
137
            if byte >= 0x20 and byte < 0x7f:
138
              asc_line += chr(byte)
139
            else:
140
              asc_line += "."
141
            hex_line += " %02x" % (byte)
142
          else:
143
            hex_line += "   "
144
          if i == 7:
145
            hex_line += " "
146
        print "%s  %s |%s|" % (reader.FormatIntPtr(addr),
147
                               hex_line,
148
                               asc_line)
149
        addr += 16
150

    
151
    if is_executable is not True and is_ascii is not True:
152
      print "%s - %s" % (reader.FormatIntPtr(start),
153
                         reader.FormatIntPtr(start + size))
154
      for slot in xrange(start,
155
                         start + size,
156
                         reader.PointerSize()):
157
        maybe_address = reader.ReadUIntPtr(slot)
158
        heap_object = heap.FindObject(maybe_address)
159
        print "%s: %s" % (reader.FormatIntPtr(slot),
160
                          reader.FormatIntPtr(maybe_address))
161
        if heap_object:
162
          heap_object.Print(Printer())
163
          print
164

    
165
  reader.ForEachMemoryRegion(dump_region)
166

    
167
# Heap constants generated by 'make grokdump' in v8heapconst module.
168
INSTANCE_TYPES = v8heapconst.INSTANCE_TYPES
169
KNOWN_MAPS = v8heapconst.KNOWN_MAPS
170
KNOWN_OBJECTS = v8heapconst.KNOWN_OBJECTS
171

    
172
# Set of structures and constants that describe the layout of minidump
173
# files. Based on MSDN and Google Breakpad.
174

    
175
MINIDUMP_HEADER = Descriptor([
176
  ("signature", ctypes.c_uint32),
177
  ("version", ctypes.c_uint32),
178
  ("stream_count", ctypes.c_uint32),
179
  ("stream_directories_rva", ctypes.c_uint32),
180
  ("checksum", ctypes.c_uint32),
181
  ("time_date_stampt", ctypes.c_uint32),
182
  ("flags", ctypes.c_uint64)
183
])
184

    
185
MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
186
  ("data_size", ctypes.c_uint32),
187
  ("rva", ctypes.c_uint32)
188
])
189

    
190
MINIDUMP_STRING = Descriptor([
191
  ("length", ctypes.c_uint32),
192
  ("buffer", lambda t: ctypes.c_uint8 * (t.length + 2))
193
])
194

    
195
MINIDUMP_DIRECTORY = Descriptor([
196
  ("stream_type", ctypes.c_uint32),
197
  ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
198
])
199

    
200
MD_EXCEPTION_MAXIMUM_PARAMETERS = 15
201

    
202
MINIDUMP_EXCEPTION = Descriptor([
203
  ("code", ctypes.c_uint32),
204
  ("flags", ctypes.c_uint32),
205
  ("record", ctypes.c_uint64),
206
  ("address", ctypes.c_uint64),
207
  ("parameter_count", ctypes.c_uint32),
208
  ("unused_alignment", ctypes.c_uint32),
209
  ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS)
210
])
211

    
212
MINIDUMP_EXCEPTION_STREAM = Descriptor([
213
  ("thread_id", ctypes.c_uint32),
214
  ("unused_alignment", ctypes.c_uint32),
215
  ("exception", MINIDUMP_EXCEPTION.ctype),
216
  ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
217
])
218

    
219
# Stream types.
220
MD_UNUSED_STREAM = 0
221
MD_RESERVED_STREAM_0 = 1
222
MD_RESERVED_STREAM_1 = 2
223
MD_THREAD_LIST_STREAM = 3
224
MD_MODULE_LIST_STREAM = 4
225
MD_MEMORY_LIST_STREAM = 5
226
MD_EXCEPTION_STREAM = 6
227
MD_SYSTEM_INFO_STREAM = 7
228
MD_THREAD_EX_LIST_STREAM = 8
229
MD_MEMORY_64_LIST_STREAM = 9
230
MD_COMMENT_STREAM_A = 10
231
MD_COMMENT_STREAM_W = 11
232
MD_HANDLE_DATA_STREAM = 12
233
MD_FUNCTION_TABLE_STREAM = 13
234
MD_UNLOADED_MODULE_LIST_STREAM = 14
235
MD_MISC_INFO_STREAM = 15
236
MD_MEMORY_INFO_LIST_STREAM = 16
237
MD_THREAD_INFO_LIST_STREAM = 17
238
MD_HANDLE_OPERATION_LIST_STREAM = 18
239

    
240
MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80
241

    
242
MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([
243
  ("control_word", ctypes.c_uint32),
244
  ("status_word", ctypes.c_uint32),
245
  ("tag_word", ctypes.c_uint32),
246
  ("error_offset", ctypes.c_uint32),
247
  ("error_selector", ctypes.c_uint32),
248
  ("data_offset", ctypes.c_uint32),
249
  ("data_selector", ctypes.c_uint32),
250
  ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE),
251
  ("cr0_npx_state", ctypes.c_uint32)
252
])
253

    
254
MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512
255

    
256
# Context flags.
257
MD_CONTEXT_X86 = 0x00010000
258
MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001)
259
MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002)
260
MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004)
261
MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008)
262
MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010)
263
MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020)
264

    
265
def EnableOnFlag(type, flag):
266
  return lambda o: [None, type][int((o.context_flags & flag) != 0)]
267

    
268
MINIDUMP_CONTEXT_X86 = Descriptor([
269
  ("context_flags", ctypes.c_uint32),
270
  # MD_CONTEXT_X86_DEBUG_REGISTERS.
271
  ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
272
  ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
273
  ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
274
  ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
275
  ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
276
  ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
277
  # MD_CONTEXT_X86_FLOATING_POINT.
278
  ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype,
279
                              MD_CONTEXT_X86_FLOATING_POINT)),
280
  # MD_CONTEXT_X86_SEGMENTS.
281
  ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
282
  ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
283
  ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
284
  ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
285
  # MD_CONTEXT_X86_INTEGER.
286
  ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
287
  ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
288
  ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
289
  ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
290
  ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
291
  ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
292
  # MD_CONTEXT_X86_CONTROL.
293
  ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
294
  ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
295
  ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
296
  ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
297
  ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
298
  ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
299
  # MD_CONTEXT_X86_EXTENDED_REGISTERS.
300
  ("extended_registers",
301
   EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE,
302
                MD_CONTEXT_X86_EXTENDED_REGISTERS))
303
])
304

    
305
MD_CONTEXT_ARM = 0x40000000
306
MD_CONTEXT_ARM_INTEGER = (MD_CONTEXT_ARM | 0x00000002)
307
MD_CONTEXT_ARM_FLOATING_POINT = (MD_CONTEXT_ARM | 0x00000004)
308
MD_FLOATINGSAVEAREA_ARM_FPR_COUNT = 32
309
MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT = 8
310

    
311
MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([
312
  ("fpscr", ctypes.c_uint64),
313
  ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPR_COUNT),
314
  ("extra", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT)
315
])
316

    
317
MINIDUMP_CONTEXT_ARM = Descriptor([
318
  ("context_flags", ctypes.c_uint32),
319
  # MD_CONTEXT_ARM_INTEGER.
320
  ("r0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
321
  ("r1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
322
  ("r2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
323
  ("r3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
324
  ("r4", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
325
  ("r5", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
326
  ("r6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
327
  ("r7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
328
  ("r8", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
329
  ("r9", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
330
  ("r10", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
331
  ("r11", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
332
  ("r12", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
333
  ("sp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
334
  ("lr", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
335
  ("pc", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
336
  ("cpsr", ctypes.c_uint32),
337
  ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype,
338
                              MD_CONTEXT_ARM_FLOATING_POINT))
339
])
340

    
341
MD_CONTEXT_AMD64 = 0x00100000
342
MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001)
343
MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002)
344
MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004)
345
MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008)
346
MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010)
347

    
348
MINIDUMP_CONTEXT_AMD64 = Descriptor([
349
  ("p1_home", ctypes.c_uint64),
350
  ("p2_home", ctypes.c_uint64),
351
  ("p3_home", ctypes.c_uint64),
352
  ("p4_home", ctypes.c_uint64),
353
  ("p5_home", ctypes.c_uint64),
354
  ("p6_home", ctypes.c_uint64),
355
  ("context_flags", ctypes.c_uint32),
356
  ("mx_csr", ctypes.c_uint32),
357
  # MD_CONTEXT_AMD64_CONTROL.
358
  ("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
359
  # MD_CONTEXT_AMD64_SEGMENTS
360
  ("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
361
  ("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
362
  ("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
363
  ("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
364
  # MD_CONTEXT_AMD64_CONTROL.
365
  ("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
366
  ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)),
367
  # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
368
  ("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
369
  ("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
370
  ("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
371
  ("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
372
  ("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
373
  ("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
374
  # MD_CONTEXT_AMD64_INTEGER.
375
  ("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
376
  ("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
377
  ("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
378
  ("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
379
  # MD_CONTEXT_AMD64_CONTROL.
380
  ("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
381
  # MD_CONTEXT_AMD64_INTEGER.
382
  ("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
383
  ("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
384
  ("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
385
  ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
386
  ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
387
  ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
388
  ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
389
  ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
390
  ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
391
  ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
392
  ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
393
  # MD_CONTEXT_AMD64_CONTROL.
394
  ("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
395
  # MD_CONTEXT_AMD64_FLOATING_POINT
396
  ("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
397
                                 MD_CONTEXT_AMD64_FLOATING_POINT)),
398
  ("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
399
                                    MD_CONTEXT_AMD64_FLOATING_POINT)),
400
  ("vector_control", EnableOnFlag(ctypes.c_uint64,
401
                                  MD_CONTEXT_AMD64_FLOATING_POINT)),
402
  # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
403
  ("debug_control", EnableOnFlag(ctypes.c_uint64,
404
                                 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
405
  ("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64,
406
                                      MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
407
  ("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64,
408
                                        MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
409
  ("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64,
410
                                         MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
411
  ("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64,
412
                                           MD_CONTEXT_AMD64_DEBUG_REGISTERS))
413
])
414

    
415
MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
416
  ("start", ctypes.c_uint64),
417
  ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
418
])
419

    
420
MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
421
  ("start", ctypes.c_uint64),
422
  ("size", ctypes.c_uint64)
423
])
424

    
425
MINIDUMP_MEMORY_LIST = Descriptor([
426
  ("range_count", ctypes.c_uint32),
427
  ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
428
])
429

    
430
MINIDUMP_MEMORY_LIST64 = Descriptor([
431
  ("range_count", ctypes.c_uint64),
432
  ("base_rva", ctypes.c_uint64),
433
  ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count)
434
])
435

    
436
MINIDUMP_THREAD = Descriptor([
437
  ("id", ctypes.c_uint32),
438
  ("suspend_count", ctypes.c_uint32),
439
  ("priority_class", ctypes.c_uint32),
440
  ("priority", ctypes.c_uint32),
441
  ("ted", ctypes.c_uint64),
442
  ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype),
443
  ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
444
])
445

    
446
MINIDUMP_THREAD_LIST = Descriptor([
447
  ("thread_count", ctypes.c_uint32),
448
  ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
449
])
450

    
451
MINIDUMP_VS_FIXEDFILEINFO = Descriptor([
452
  ("dwSignature", ctypes.c_uint32),
453
  ("dwStrucVersion", ctypes.c_uint32),
454
  ("dwFileVersionMS", ctypes.c_uint32),
455
  ("dwFileVersionLS", ctypes.c_uint32),
456
  ("dwProductVersionMS", ctypes.c_uint32),
457
  ("dwProductVersionLS", ctypes.c_uint32),
458
  ("dwFileFlagsMask", ctypes.c_uint32),
459
  ("dwFileFlags", ctypes.c_uint32),
460
  ("dwFileOS", ctypes.c_uint32),
461
  ("dwFileType", ctypes.c_uint32),
462
  ("dwFileSubtype", ctypes.c_uint32),
463
  ("dwFileDateMS", ctypes.c_uint32),
464
  ("dwFileDateLS", ctypes.c_uint32)
465
])
466

    
467
MINIDUMP_RAW_MODULE = Descriptor([
468
  ("base_of_image", ctypes.c_uint64),
469
  ("size_of_image", ctypes.c_uint32),
470
  ("checksum", ctypes.c_uint32),
471
  ("time_date_stamp", ctypes.c_uint32),
472
  ("module_name_rva", ctypes.c_uint32),
473
  ("version_info", MINIDUMP_VS_FIXEDFILEINFO.ctype),
474
  ("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
475
  ("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
476
  ("reserved0", ctypes.c_uint32 * 2),
477
  ("reserved1", ctypes.c_uint32 * 2)
478
])
479

    
480
MINIDUMP_MODULE_LIST = Descriptor([
481
  ("number_of_modules", ctypes.c_uint32),
482
  ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules)
483
])
484

    
485
MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
486
  ("processor_architecture", ctypes.c_uint16)
487
])
488

    
489
MD_CPU_ARCHITECTURE_X86 = 0
490
MD_CPU_ARCHITECTURE_ARM = 5
491
MD_CPU_ARCHITECTURE_AMD64 = 9
492

    
493
class FuncSymbol:
494
  def __init__(self, start, size, name):
495
    self.start = start
496
    self.end = self.start + size
497
    self.name = name
498

    
499
  def __cmp__(self, other):
500
    if isinstance(other, FuncSymbol):
501
      return self.start - other.start
502
    return self.start - other
503

    
504
  def Covers(self, addr):
505
    return (self.start <= addr) and (addr < self.end)
506

    
507
class MinidumpReader(object):
508
  """Minidump (.dmp) reader."""
509

    
510
  _HEADER_MAGIC = 0x504d444d
511

    
512
  def __init__(self, options, minidump_name):
513
    self.minidump_name = minidump_name
514
    self.minidump_file = open(minidump_name, "r")
515
    self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE)
516
    self.header = MINIDUMP_HEADER.Read(self.minidump, 0)
517
    if self.header.signature != MinidumpReader._HEADER_MAGIC:
518
      print >>sys.stderr, "Warning: Unsupported minidump header magic!"
519
    DebugPrint(self.header)
520
    directories = []
521
    offset = self.header.stream_directories_rva
522
    for _ in xrange(self.header.stream_count):
523
      directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
524
      offset += MINIDUMP_DIRECTORY.size
525
    self.arch = None
526
    self.exception = None
527
    self.exception_context = None
528
    self.memory_list = None
529
    self.memory_list64 = None
530
    self.module_list = None
531
    self.thread_map = {}
532

    
533
    self.symdir = options.symdir
534
    self.modules_with_symbols = []
535
    self.symbols = []
536

    
537
    # Find MDRawSystemInfo stream and determine arch.
538
    for d in directories:
539
      if d.stream_type == MD_SYSTEM_INFO_STREAM:
540
        system_info = MINIDUMP_RAW_SYSTEM_INFO.Read(
541
            self.minidump, d.location.rva)
542
        self.arch = system_info.processor_architecture
543
        assert self.arch in [MD_CPU_ARCHITECTURE_AMD64,
544
                             MD_CPU_ARCHITECTURE_ARM,
545
                             MD_CPU_ARCHITECTURE_X86]
546
    assert not self.arch is None
547

    
548
    for d in directories:
549
      DebugPrint(d)
550
      if d.stream_type == MD_EXCEPTION_STREAM:
551
        self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
552
          self.minidump, d.location.rva)
553
        DebugPrint(self.exception)
554
        if self.arch == MD_CPU_ARCHITECTURE_X86:
555
          self.exception_context = MINIDUMP_CONTEXT_X86.Read(
556
              self.minidump, self.exception.thread_context.rva)
557
        elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
558
          self.exception_context = MINIDUMP_CONTEXT_AMD64.Read(
559
              self.minidump, self.exception.thread_context.rva)
560
        elif self.arch == MD_CPU_ARCHITECTURE_ARM:
561
          self.exception_context = MINIDUMP_CONTEXT_ARM.Read(
562
              self.minidump, self.exception.thread_context.rva)
563
        DebugPrint(self.exception_context)
564
      elif d.stream_type == MD_THREAD_LIST_STREAM:
565
        thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
566
        assert ctypes.sizeof(thread_list) == d.location.data_size
567
        DebugPrint(thread_list)
568
        for thread in thread_list.threads:
569
          DebugPrint(thread)
570
          self.thread_map[thread.id] = thread
571
      elif d.stream_type == MD_MODULE_LIST_STREAM:
572
        assert self.module_list is None
573
        self.module_list = MINIDUMP_MODULE_LIST.Read(
574
          self.minidump, d.location.rva)
575
        assert ctypes.sizeof(self.module_list) == d.location.data_size
576
      elif d.stream_type == MD_MEMORY_LIST_STREAM:
577
        print >>sys.stderr, "Warning: This is not a full minidump!"
578
        assert self.memory_list is None
579
        self.memory_list = MINIDUMP_MEMORY_LIST.Read(
580
          self.minidump, d.location.rva)
581
        assert ctypes.sizeof(self.memory_list) == d.location.data_size
582
        DebugPrint(self.memory_list)
583
      elif d.stream_type == MD_MEMORY_64_LIST_STREAM:
584
        assert self.memory_list64 is None
585
        self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read(
586
          self.minidump, d.location.rva)
587
        assert ctypes.sizeof(self.memory_list64) == d.location.data_size
588
        DebugPrint(self.memory_list64)
589

    
590
  def IsValidAddress(self, address):
591
    return self.FindLocation(address) is not None
592

    
593
  def ReadU8(self, address):
594
    location = self.FindLocation(address)
595
    return ctypes.c_uint8.from_buffer(self.minidump, location).value
596

    
597
  def ReadU32(self, address):
598
    location = self.FindLocation(address)
599
    return ctypes.c_uint32.from_buffer(self.minidump, location).value
600

    
601
  def ReadU64(self, address):
602
    location = self.FindLocation(address)
603
    return ctypes.c_uint64.from_buffer(self.minidump, location).value
604

    
605
  def ReadUIntPtr(self, address):
606
    if self.arch == MD_CPU_ARCHITECTURE_AMD64:
607
      return self.ReadU64(address)
608
    elif self.arch == MD_CPU_ARCHITECTURE_ARM:
609
      return self.ReadU32(address)
610
    elif self.arch == MD_CPU_ARCHITECTURE_X86:
611
      return self.ReadU32(address)
612

    
613
  def ReadBytes(self, address, size):
614
    location = self.FindLocation(address)
615
    return self.minidump[location:location + size]
616

    
617
  def _ReadWord(self, location):
618
    if self.arch == MD_CPU_ARCHITECTURE_AMD64:
619
      return ctypes.c_uint64.from_buffer(self.minidump, location).value
620
    elif self.arch == MD_CPU_ARCHITECTURE_ARM:
621
      return ctypes.c_uint32.from_buffer(self.minidump, location).value
622
    elif self.arch == MD_CPU_ARCHITECTURE_X86:
623
      return ctypes.c_uint32.from_buffer(self.minidump, location).value
624

    
625
  def IsProbableASCIIRegion(self, location, length):
626
    ascii_bytes = 0
627
    non_ascii_bytes = 0
628
    for loc in xrange(location, location + length):
629
      byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
630
      if byte >= 0x7f:
631
        non_ascii_bytes += 1
632
      if byte < 0x20 and byte != 0:
633
        non_ascii_bytes += 1
634
      if byte < 0x7f and byte >= 0x20:
635
        ascii_bytes += 1
636
      if byte == 0xa:  # newline
637
        ascii_bytes += 1
638
    if ascii_bytes * 10 <= length:
639
      return False
640
    if length > 0 and ascii_bytes > non_ascii_bytes * 7:
641
      return True
642
    if ascii_bytes > non_ascii_bytes * 3:
643
      return None  # Maybe
644
    return False
645

    
646
  def IsProbableExecutableRegion(self, location, length):
647
    opcode_bytes = 0
648
    sixty_four = self.arch == MD_CPU_ARCHITECTURE_AMD64
649
    for loc in xrange(location, location + length):
650
      byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
651
      if (byte == 0x8b or           # mov
652
          byte == 0x89 or           # mov reg-reg
653
          (byte & 0xf0) == 0x50 or  # push/pop
654
          (sixty_four and (byte & 0xf0) == 0x40) or  # rex prefix
655
          byte == 0xc3 or           # return
656
          byte == 0x74 or           # jeq
657
          byte == 0x84 or           # jeq far
658
          byte == 0x75 or           # jne
659
          byte == 0x85 or           # jne far
660
          byte == 0xe8 or           # call
661
          byte == 0xe9 or           # jmp far
662
          byte == 0xeb):            # jmp near
663
        opcode_bytes += 1
664
    opcode_percent = (opcode_bytes * 100) / length
665
    threshold = 20
666
    if opcode_percent > threshold + 2:
667
      return True
668
    if opcode_percent > threshold - 2:
669
      return None  # Maybe
670
    return False
671

    
672
  def FindRegion(self, addr):
673
    answer = [-1, -1]
674
    def is_in(reader, start, size, location):
675
      if addr >= start and addr < start + size:
676
        answer[0] = start
677
        answer[1] = size
678
    self.ForEachMemoryRegion(is_in)
679
    if answer[0] == -1:
680
      return None
681
    return answer
682

    
683
  def ForEachMemoryRegion(self, cb):
684
    if self.memory_list64 is not None:
685
      for r in self.memory_list64.ranges:
686
        location = self.memory_list64.base_rva + offset
687
        cb(self, r.start, r.size, location)
688
        offset += r.size
689

    
690
    if self.memory_list is not None:
691
      for r in self.memory_list.ranges:
692
        cb(self, r.start, r.memory.data_size, r.memory.rva)
693

    
694
  def FindWord(self, word, alignment=0):
695
    def search_inside_region(reader, start, size, location):
696
      location = (location + alignment) & ~alignment
697
      for loc in xrange(location, location + size - self.PointerSize()):
698
        if reader._ReadWord(loc) == word:
699
          slot = start + (loc - location)
700
          print "%s: %s" % (reader.FormatIntPtr(slot),
701
                            reader.FormatIntPtr(word))
702
    self.ForEachMemoryRegion(search_inside_region)
703

    
704
  def FindLocation(self, address):
705
    offset = 0
706
    if self.memory_list64 is not None:
707
      for r in self.memory_list64.ranges:
708
        if r.start <= address < r.start + r.size:
709
          return self.memory_list64.base_rva + offset + address - r.start
710
        offset += r.size
711
    if self.memory_list is not None:
712
      for r in self.memory_list.ranges:
713
        if r.start <= address < r.start + r.memory.data_size:
714
          return r.memory.rva + address - r.start
715
    return None
716

    
717
  def GetDisasmLines(self, address, size):
718
    def CountUndefinedInstructions(lines):
719
      pattern = "<UNDEFINED>"
720
      return sum([line.count(pattern) for (ignore, line) in lines])
721

    
722
    location = self.FindLocation(address)
723
    if location is None: return []
724
    arch = None
725
    possible_objdump_flags = [""]
726
    if self.arch == MD_CPU_ARCHITECTURE_X86:
727
      arch = "ia32"
728
    elif self.arch == MD_CPU_ARCHITECTURE_ARM:
729
      arch = "arm"
730
      possible_objdump_flags = ["", "--disassembler-options=force-thumb"]
731
    elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
732
      arch = "x64"
733
    results = [ disasm.GetDisasmLines(self.minidump_name,
734
                                     location,
735
                                     size,
736
                                     arch,
737
                                     False,
738
                                     objdump_flags)
739
                for objdump_flags in possible_objdump_flags ]
740
    return min(results, key=CountUndefinedInstructions)
741

    
742

    
743
  def Dispose(self):
744
    self.minidump.close()
745
    self.minidump_file.close()
746

    
747
  def ExceptionIP(self):
748
    if self.arch == MD_CPU_ARCHITECTURE_AMD64:
749
      return self.exception_context.rip
750
    elif self.arch == MD_CPU_ARCHITECTURE_ARM:
751
      return self.exception_context.pc
752
    elif self.arch == MD_CPU_ARCHITECTURE_X86:
753
      return self.exception_context.eip
754

    
755
  def ExceptionSP(self):
756
    if self.arch == MD_CPU_ARCHITECTURE_AMD64:
757
      return self.exception_context.rsp
758
    elif self.arch == MD_CPU_ARCHITECTURE_ARM:
759
      return self.exception_context.sp
760
    elif self.arch == MD_CPU_ARCHITECTURE_X86:
761
      return self.exception_context.esp
762

    
763
  def ExceptionFP(self):
764
    if self.arch == MD_CPU_ARCHITECTURE_AMD64:
765
      return self.exception_context.rbp
766
    elif self.arch == MD_CPU_ARCHITECTURE_ARM:
767
      return None
768
    elif self.arch == MD_CPU_ARCHITECTURE_X86:
769
      return self.exception_context.ebp
770

    
771
  def FormatIntPtr(self, value):
772
    if self.arch == MD_CPU_ARCHITECTURE_AMD64:
773
      return "%016x" % value
774
    elif self.arch == MD_CPU_ARCHITECTURE_ARM:
775
      return "%08x" % value
776
    elif self.arch == MD_CPU_ARCHITECTURE_X86:
777
      return "%08x" % value
778

    
779
  def PointerSize(self):
780
    if self.arch == MD_CPU_ARCHITECTURE_AMD64:
781
      return 8
782
    elif self.arch == MD_CPU_ARCHITECTURE_ARM:
783
      return 4
784
    elif self.arch == MD_CPU_ARCHITECTURE_X86:
785
      return 4
786

    
787
  def Register(self, name):
788
    return self.exception_context.__getattribute__(name)
789

    
790
  def ReadMinidumpString(self, rva):
791
    string = bytearray(MINIDUMP_STRING.Read(self.minidump, rva).buffer)
792
    string = string.decode("utf16")
793
    return string[0:len(string) - 1]
794

    
795
  # Load FUNC records from a BreakPad symbol file
796
  #
797
  #    http://code.google.com/p/google-breakpad/wiki/SymbolFiles
798
  #
799
  def _LoadSymbolsFrom(self, symfile, baseaddr):
800
    print "Loading symbols from %s" % (symfile)
801
    funcs = []
802
    with open(symfile) as f:
803
      for line in f:
804
        result = re.match(
805
            r"^FUNC ([a-f0-9]+) ([a-f0-9]+) ([a-f0-9]+) (.*)$", line)
806
        if result is not None:
807
          start = int(result.group(1), 16)
808
          size = int(result.group(2), 16)
809
          name = result.group(4).rstrip()
810
          bisect.insort_left(self.symbols,
811
                             FuncSymbol(baseaddr + start, size, name))
812
    print " ... done"
813

    
814
  def TryLoadSymbolsFor(self, modulename, module):
815
    try:
816
      symfile = os.path.join(self.symdir,
817
                             modulename.replace('.', '_') + ".pdb.sym")
818
      if os.path.isfile(symfile):
819
        self._LoadSymbolsFrom(symfile, module.base_of_image)
820
        self.modules_with_symbols.append(module)
821
    except Exception as e:
822
      print "  ... failure (%s)" % (e)
823

    
824
  # Returns true if address is covered by some module that has loaded symbols.
825
  def _IsInModuleWithSymbols(self, addr):
826
    for module in self.modules_with_symbols:
827
      start = module.base_of_image
828
      end = start + module.size_of_image
829
      if (start <= addr) and (addr < end):
830
        return True
831
    return False
832

    
833
  # Find symbol covering the given address and return its name in format
834
  #     <symbol name>+<offset from the start>
835
  def FindSymbol(self, addr):
836
    if not self._IsInModuleWithSymbols(addr):
837
      return None
838

    
839
    i = bisect.bisect_left(self.symbols, addr)
840
    symbol = None
841
    if (0 < i) and self.symbols[i - 1].Covers(addr):
842
      symbol = self.symbols[i - 1]
843
    elif (i < len(self.symbols)) and self.symbols[i].Covers(addr):
844
      symbol = self.symbols[i]
845
    else:
846
      return None
847
    diff = addr - symbol.start
848
    return "%s+0x%x" % (symbol.name, diff)
849

    
850

    
851
class Printer(object):
852
  """Printer with indentation support."""
853

    
854
  def __init__(self):
855
    self.indent = 0
856

    
857
  def Indent(self):
858
    self.indent += 2
859

    
860
  def Dedent(self):
861
    self.indent -= 2
862

    
863
  def Print(self, string):
864
    print "%s%s" % (self._IndentString(), string)
865

    
866
  def PrintLines(self, lines):
867
    indent = self._IndentString()
868
    print "\n".join("%s%s" % (indent, line) for line in lines)
869

    
870
  def _IndentString(self):
871
    return self.indent * " "
872

    
873

    
874
ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
875

    
876

    
877
def FormatDisasmLine(start, heap, line):
878
  line_address = start + line[0]
879
  stack_slot = heap.stack_map.get(line_address)
880
  marker = "  "
881
  if stack_slot:
882
    marker = "=>"
883
  code = AnnotateAddresses(heap, line[1])
884
  return "%s%08x %08x: %s" % (marker, line_address, line[0], code)
885

    
886

    
887
def AnnotateAddresses(heap, line):
888
  extra = []
889
  for m in ADDRESS_RE.finditer(line):
890
    maybe_address = int(m.group(0), 16)
891
    object = heap.FindObject(maybe_address)
892
    if not object: continue
893
    extra.append(str(object))
894
  if len(extra) == 0: return line
895
  return "%s  ;; %s" % (line, ", ".join(extra))
896

    
897

    
898
class HeapObject(object):
899
  def __init__(self, heap, map, address):
900
    self.heap = heap
901
    self.map = map
902
    self.address = address
903

    
904
  def Is(self, cls):
905
    return isinstance(self, cls)
906

    
907
  def Print(self, p):
908
    p.Print(str(self))
909

    
910
  def __str__(self):
911
    return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address),
912
                                   INSTANCE_TYPES[self.map.instance_type])
913

    
914
  def ObjectField(self, offset):
915
    field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
916
    return self.heap.FindObjectOrSmi(field_value)
917

    
918
  def SmiField(self, offset):
919
    field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
920
    assert (field_value & 1) == 0
921
    return field_value / 2
922

    
923

    
924
class Map(HeapObject):
925
  def Decode(self, offset, size, value):
926
    return (value >> offset) & ((1 << size) - 1)
927

    
928
  # Instance Sizes
929
  def InstanceSizesOffset(self):
930
    return self.heap.PointerSize()
931

    
932
  def InstanceSizeOffset(self):
933
    return self.InstanceSizesOffset()
934

    
935
  def InObjectProperties(self):
936
    return self.InstanceSizeOffset() + 1
937

    
938
  def PreAllocatedPropertyFields(self):
939
    return self.InObjectProperties() + 1
940

    
941
  def VisitorId(self):
942
    return self.PreAllocatedPropertyFields() + 1
943

    
944
  # Instance Attributes
945
  def InstanceAttributesOffset(self):
946
    return self.InstanceSizesOffset() + self.heap.IntSize()
947

    
948
  def InstanceTypeOffset(self):
949
    return self.InstanceAttributesOffset()
950

    
951
  def UnusedPropertyFieldsOffset(self):
952
    return self.InstanceTypeOffset() + 1
953

    
954
  def BitFieldOffset(self):
955
    return self.UnusedPropertyFieldsOffset() + 1
956

    
957
  def BitField2Offset(self):
958
    return self.BitFieldOffset() + 1
959

    
960
  # Other fields
961
  def PrototypeOffset(self):
962
    return self.InstanceAttributesOffset() + self.heap.IntSize()
963

    
964
  def ConstructorOffset(self):
965
    return self.PrototypeOffset() + self.heap.PointerSize()
966

    
967
  def TransitionsOrBackPointerOffset(self):
968
    return self.ConstructorOffset() + self.heap.PointerSize()
969

    
970
  def DescriptorsOffset(self):
971
    return self.TransitionsOrBackPointerOffset() + self.heap.PointerSize()
972

    
973
  def CodeCacheOffset(self):
974
    return self.DescriptorsOffset() + self.heap.PointerSize()
975

    
976
  def DependentCodeOffset(self):
977
    return self.CodeCacheOffset() + self.heap.PointerSize()
978

    
979
  def BitField3Offset(self):
980
    return self.DependentCodeOffset() + self.heap.PointerSize()
981

    
982
  def ReadByte(self, offset):
983
    return self.heap.reader.ReadU8(self.address + offset)
984

    
985
  def Print(self, p):
986
    p.Print("Map(%08x)" % (self.address))
987
    p.Print("- size: %d, inobject: %d, preallocated: %d, visitor: %d" % (
988
        self.ReadByte(self.InstanceSizeOffset()),
989
        self.ReadByte(self.InObjectProperties()),
990
        self.ReadByte(self.PreAllocatedPropertyFields()),
991
        self.VisitorId()))
992

    
993
    bitfield = self.ReadByte(self.BitFieldOffset())
994
    bitfield2 = self.ReadByte(self.BitField2Offset())
995
    p.Print("- %s, unused: %d, bf: %d, bf2: %d" % (
996
        INSTANCE_TYPES[self.ReadByte(self.InstanceTypeOffset())],
997
        self.ReadByte(self.UnusedPropertyFieldsOffset()),
998
        bitfield, bitfield2))
999

    
1000
    p.Print("- kind: %s" % (self.Decode(3, 5, bitfield2)))
1001

    
1002
    bitfield3 = self.ObjectField(self.BitField3Offset())
1003
    p.Print(
1004
        "- EnumLength: %d NumberOfOwnDescriptors: %d OwnsDescriptors: %s" % (
1005
            self.Decode(0, 11, bitfield3),
1006
            self.Decode(11, 11, bitfield3),
1007
            self.Decode(25, 1, bitfield3)))
1008
    p.Print("- IsShared: %s" % (self.Decode(22, 1, bitfield3)))
1009
    p.Print("- FunctionWithPrototype: %s" % (self.Decode(23, 1, bitfield3)))
1010
    p.Print("- DictionaryMap: %s" % (self.Decode(24, 1, bitfield3)))
1011

    
1012
    descriptors = self.ObjectField(self.DescriptorsOffset())
1013
    if descriptors.__class__ == FixedArray:
1014
      DescriptorArray(descriptors).Print(p)
1015
    else:
1016
      p.Print("Descriptors: %s" % (descriptors))
1017

    
1018
    transitions = self.ObjectField(self.TransitionsOrBackPointerOffset())
1019
    if transitions.__class__ == FixedArray:
1020
      TransitionArray(transitions).Print(p)
1021
    else:
1022
      p.Print("TransitionsOrBackPointer: %s" % (transitions))
1023

    
1024
  def __init__(self, heap, map, address):
1025
    HeapObject.__init__(self, heap, map, address)
1026
    self.instance_type = \
1027
        heap.reader.ReadU8(self.address + self.InstanceTypeOffset())
1028

    
1029

    
1030
class String(HeapObject):
1031
  def LengthOffset(self):
1032
    # First word after the map is the hash, the second is the length.
1033
    return self.heap.PointerSize() * 2
1034

    
1035
  def __init__(self, heap, map, address):
1036
    HeapObject.__init__(self, heap, map, address)
1037
    self.length = self.SmiField(self.LengthOffset())
1038

    
1039
  def GetChars(self):
1040
    return "?string?"
1041

    
1042
  def Print(self, p):
1043
    p.Print(str(self))
1044

    
1045
  def __str__(self):
1046
    return "\"%s\"" % self.GetChars()
1047

    
1048

    
1049
class SeqString(String):
1050
  def CharsOffset(self):
1051
    return self.heap.PointerSize() * 3
1052

    
1053
  def __init__(self, heap, map, address):
1054
    String.__init__(self, heap, map, address)
1055
    self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(),
1056
                                       self.length)
1057

    
1058
  def GetChars(self):
1059
    return self.chars
1060

    
1061

    
1062
class ExternalString(String):
1063
  # TODO(vegorov) fix ExternalString for X64 architecture
1064
  RESOURCE_OFFSET = 12
1065

    
1066
  WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
1067
  WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
1068

    
1069
  def __init__(self, heap, map, address):
1070
    String.__init__(self, heap, map, address)
1071
    reader = heap.reader
1072
    self.resource = \
1073
        reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
1074
    self.chars = "?external string?"
1075
    if not reader.IsValidAddress(self.resource): return
1076
    string_impl_address = self.resource + \
1077
        ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
1078
    if not reader.IsValidAddress(string_impl_address): return
1079
    string_impl = reader.ReadU32(string_impl_address)
1080
    chars_ptr_address = string_impl + \
1081
        ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
1082
    if not reader.IsValidAddress(chars_ptr_address): return
1083
    chars_ptr = reader.ReadU32(chars_ptr_address)
1084
    if not reader.IsValidAddress(chars_ptr): return
1085
    raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
1086
    self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
1087

    
1088
  def GetChars(self):
1089
    return self.chars
1090

    
1091

    
1092
class ConsString(String):
1093
  def LeftOffset(self):
1094
    return self.heap.PointerSize() * 3
1095

    
1096
  def RightOffset(self):
1097
    return self.heap.PointerSize() * 4
1098

    
1099
  def __init__(self, heap, map, address):
1100
    String.__init__(self, heap, map, address)
1101
    self.left = self.ObjectField(self.LeftOffset())
1102
    self.right = self.ObjectField(self.RightOffset())
1103

    
1104
  def GetChars(self):
1105
    try:
1106
      return self.left.GetChars() + self.right.GetChars()
1107
    except:
1108
      return "***CAUGHT EXCEPTION IN GROKDUMP***"
1109

    
1110

    
1111
class Oddball(HeapObject):
1112
  # Should match declarations in objects.h
1113
  KINDS = [
1114
    "False",
1115
    "True",
1116
    "TheHole",
1117
    "Null",
1118
    "ArgumentMarker",
1119
    "Undefined",
1120
    "Other"
1121
  ]
1122

    
1123
  def ToStringOffset(self):
1124
    return self.heap.PointerSize()
1125

    
1126
  def ToNumberOffset(self):
1127
    return self.ToStringOffset() + self.heap.PointerSize()
1128

    
1129
  def KindOffset(self):
1130
    return self.ToNumberOffset() + self.heap.PointerSize()
1131

    
1132
  def __init__(self, heap, map, address):
1133
    HeapObject.__init__(self, heap, map, address)
1134
    self.to_string = self.ObjectField(self.ToStringOffset())
1135
    self.kind = self.SmiField(self.KindOffset())
1136

    
1137
  def Print(self, p):
1138
    p.Print(str(self))
1139

    
1140
  def __str__(self):
1141
    if self.to_string:
1142
      return "Oddball(%08x, <%s>)" % (self.address, str(self.to_string))
1143
    else:
1144
      kind = "???"
1145
      if 0 <= self.kind < len(Oddball.KINDS):
1146
        kind = Oddball.KINDS[self.kind]
1147
      return "Oddball(%08x, kind=%s)" % (self.address, kind)
1148

    
1149

    
1150
class FixedArray(HeapObject):
1151
  def LengthOffset(self):
1152
    return self.heap.PointerSize()
1153

    
1154
  def ElementsOffset(self):
1155
    return self.heap.PointerSize() * 2
1156

    
1157
  def MemberOffset(self, i):
1158
    return self.ElementsOffset() + self.heap.PointerSize() * i
1159

    
1160
  def Get(self, i):
1161
    return self.ObjectField(self.MemberOffset(i))
1162

    
1163
  def __init__(self, heap, map, address):
1164
    HeapObject.__init__(self, heap, map, address)
1165
    self.length = self.SmiField(self.LengthOffset())
1166

    
1167
  def Print(self, p):
1168
    p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1169
    p.Indent()
1170
    p.Print("length: %d" % self.length)
1171
    base_offset = self.ElementsOffset()
1172
    for i in xrange(self.length):
1173
      offset = base_offset + 4 * i
1174
      try:
1175
        p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
1176
      except TypeError:
1177
        p.Dedent()
1178
        p.Print("...")
1179
        p.Print("}")
1180
        return
1181
    p.Dedent()
1182
    p.Print("}")
1183

    
1184
  def __str__(self):
1185
    return "FixedArray(%08x, length=%d)" % (self.address, self.length)
1186

    
1187

    
1188
class DescriptorArray(object):
1189
  def __init__(self, array):
1190
    self.array = array
1191

    
1192
  def Length(self):
1193
    return self.array.Get(0)
1194

    
1195
  def Decode(self, offset, size, value):
1196
    return (value >> offset) & ((1 << size) - 1)
1197

    
1198
  TYPES = [
1199
      "normal",
1200
      "field",
1201
      "function",
1202
      "callbacks"
1203
  ]
1204

    
1205
  def Type(self, value):
1206
    return DescriptorArray.TYPES[self.Decode(0, 3, value)]
1207

    
1208
  def Attributes(self, value):
1209
    attributes = self.Decode(3, 3, value)
1210
    result = []
1211
    if (attributes & 0): result += ["ReadOnly"]
1212
    if (attributes & 1): result += ["DontEnum"]
1213
    if (attributes & 2): result += ["DontDelete"]
1214
    return "[" + (",".join(result)) + "]"
1215

    
1216
  def Deleted(self, value):
1217
    return self.Decode(6, 1, value) == 1
1218

    
1219
  def FieldIndex(self, value):
1220
    return self.Decode(20, 11, value)
1221

    
1222
  def Pointer(self, value):
1223
    return self.Decode(6, 11, value)
1224

    
1225
  def Details(self, di, value):
1226
    return (
1227
        di,
1228
        self.Type(value),
1229
        self.Attributes(value),
1230
        self.FieldIndex(value),
1231
        self.Pointer(value)
1232
    )
1233

    
1234

    
1235
  def Print(self, p):
1236
    length = self.Length()
1237
    array = self.array
1238

    
1239
    p.Print("Descriptors(%08x, length=%d)" % (array.address, length))
1240
    p.Print("[et] %s" % (array.Get(1)))
1241

    
1242
    for di in xrange(length):
1243
      i = 2 + di * 3
1244
      p.Print("0x%x" % (array.address + array.MemberOffset(i)))
1245
      p.Print("[%i] name:    %s" % (di, array.Get(i + 0)))
1246
      p.Print("[%i] details: %s %s field-index %i pointer %i" % \
1247
              self.Details(di, array.Get(i + 1)))
1248
      p.Print("[%i] value:   %s" % (di, array.Get(i + 2)))
1249

    
1250
    end = self.array.length // 3
1251
    if length != end:
1252
      p.Print("[%i-%i] slack descriptors" % (length, end))
1253

    
1254

    
1255
class TransitionArray(object):
1256
  def __init__(self, array):
1257
    self.array = array
1258

    
1259
  def IsSimpleTransition(self):
1260
    return self.array.length <= 2
1261

    
1262
  def Length(self):
1263
    # SimpleTransition cases
1264
    if self.IsSimpleTransition():
1265
      return self.array.length - 1
1266
    return (self.array.length - 3) // 2
1267

    
1268
  def Print(self, p):
1269
    length = self.Length()
1270
    array = self.array
1271

    
1272
    p.Print("Transitions(%08x, length=%d)" % (array.address, length))
1273
    p.Print("[backpointer] %s" % (array.Get(0)))
1274
    if self.IsSimpleTransition():
1275
      if length == 1:
1276
        p.Print("[simple target] %s" % (array.Get(1)))
1277
      return
1278

    
1279
    elements = array.Get(1)
1280
    if elements is not None:
1281
      p.Print("[elements   ] %s" % (elements))
1282

    
1283
    prototype = array.Get(2)
1284
    if prototype is not None:
1285
      p.Print("[prototype  ] %s" % (prototype))
1286

    
1287
    for di in xrange(length):
1288
      i = 3 + di * 2
1289
      p.Print("[%i] symbol: %s" % (di, array.Get(i + 0)))
1290
      p.Print("[%i] target: %s" % (di, array.Get(i + 1)))
1291

    
1292

    
1293
class JSFunction(HeapObject):
1294
  def CodeEntryOffset(self):
1295
    return 3 * self.heap.PointerSize()
1296

    
1297
  def SharedOffset(self):
1298
    return 5 * self.heap.PointerSize()
1299

    
1300
  def __init__(self, heap, map, address):
1301
    HeapObject.__init__(self, heap, map, address)
1302
    code_entry = \
1303
        heap.reader.ReadU32(self.address + self.CodeEntryOffset())
1304
    self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1)
1305
    self.shared = self.ObjectField(self.SharedOffset())
1306

    
1307
  def Print(self, p):
1308
    source = "\n".join("  %s" % line for line in self._GetSource().split("\n"))
1309
    p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1310
    p.Indent()
1311
    p.Print("inferred name: %s" % self.shared.inferred_name)
1312
    if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
1313
      p.Print("script name: %s" % self.shared.script.name)
1314
    p.Print("source:")
1315
    p.PrintLines(self._GetSource().split("\n"))
1316
    p.Print("code:")
1317
    self.code.Print(p)
1318
    if self.code != self.shared.code:
1319
      p.Print("unoptimized code:")
1320
      self.shared.code.Print(p)
1321
    p.Dedent()
1322
    p.Print("}")
1323

    
1324
  def __str__(self):
1325
    inferred_name = ""
1326
    if self.shared.Is(SharedFunctionInfo):
1327
      inferred_name = self.shared.inferred_name
1328
    return "JSFunction(%s, %s)" % \
1329
          (self.heap.reader.FormatIntPtr(self.address), inferred_name)
1330

    
1331
  def _GetSource(self):
1332
    source = "?source?"
1333
    start = self.shared.start_position
1334
    end = self.shared.end_position
1335
    if not self.shared.script.Is(Script): return source
1336
    script_source = self.shared.script.source
1337
    if not script_source.Is(String): return source
1338
    return script_source.GetChars()[start:end]
1339

    
1340

    
1341
class SharedFunctionInfo(HeapObject):
1342
  def CodeOffset(self):
1343
    return 2 * self.heap.PointerSize()
1344

    
1345
  def ScriptOffset(self):
1346
    return 7 * self.heap.PointerSize()
1347

    
1348
  def InferredNameOffset(self):
1349
    return 9 * self.heap.PointerSize()
1350

    
1351
  def EndPositionOffset(self):
1352
    return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
1353

    
1354
  def StartPositionAndTypeOffset(self):
1355
    return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
1356

    
1357
  def __init__(self, heap, map, address):
1358
    HeapObject.__init__(self, heap, map, address)
1359
    self.code = self.ObjectField(self.CodeOffset())
1360
    self.script = self.ObjectField(self.ScriptOffset())
1361
    self.inferred_name = self.ObjectField(self.InferredNameOffset())
1362
    if heap.PointerSize() == 8:
1363
      start_position_and_type = \
1364
          heap.reader.ReadU32(self.StartPositionAndTypeOffset())
1365
      self.start_position = start_position_and_type >> 2
1366
      pseudo_smi_end_position = \
1367
          heap.reader.ReadU32(self.EndPositionOffset())
1368
      self.end_position = pseudo_smi_end_position >> 2
1369
    else:
1370
      start_position_and_type = \
1371
          self.SmiField(self.StartPositionAndTypeOffset())
1372
      self.start_position = start_position_and_type >> 2
1373
      self.end_position = \
1374
          self.SmiField(self.EndPositionOffset())
1375

    
1376

    
1377
class Script(HeapObject):
1378
  def SourceOffset(self):
1379
    return self.heap.PointerSize()
1380

    
1381
  def NameOffset(self):
1382
    return self.SourceOffset() + self.heap.PointerSize()
1383

    
1384
  def __init__(self, heap, map, address):
1385
    HeapObject.__init__(self, heap, map, address)
1386
    self.source = self.ObjectField(self.SourceOffset())
1387
    self.name = self.ObjectField(self.NameOffset())
1388

    
1389

    
1390
class CodeCache(HeapObject):
1391
  def DefaultCacheOffset(self):
1392
    return self.heap.PointerSize()
1393

    
1394
  def NormalTypeCacheOffset(self):
1395
    return self.DefaultCacheOffset() + self.heap.PointerSize()
1396

    
1397
  def __init__(self, heap, map, address):
1398
    HeapObject.__init__(self, heap, map, address)
1399
    self.default_cache = self.ObjectField(self.DefaultCacheOffset())
1400
    self.normal_type_cache = self.ObjectField(self.NormalTypeCacheOffset())
1401

    
1402
  def Print(self, p):
1403
    p.Print("CodeCache(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1404
    p.Indent()
1405
    p.Print("default cache: %s" % self.default_cache)
1406
    p.Print("normal type cache: %s" % self.normal_type_cache)
1407
    p.Dedent()
1408
    p.Print("}")
1409

    
1410

    
1411
class Code(HeapObject):
1412
  CODE_ALIGNMENT_MASK = (1 << 5) - 1
1413

    
1414
  def InstructionSizeOffset(self):
1415
    return self.heap.PointerSize()
1416

    
1417
  @staticmethod
1418
  def HeaderSize(heap):
1419
    return (heap.PointerSize() + heap.IntSize() + \
1420
        4 * heap.PointerSize() + 3 * heap.IntSize() + \
1421
        Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK
1422

    
1423
  def __init__(self, heap, map, address):
1424
    HeapObject.__init__(self, heap, map, address)
1425
    self.entry = self.address + Code.HeaderSize(heap)
1426
    self.instruction_size = \
1427
        heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
1428

    
1429
  def Print(self, p):
1430
    lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
1431
    p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1432
    p.Indent()
1433
    p.Print("instruction_size: %d" % self.instruction_size)
1434
    p.PrintLines(self._FormatLine(line) for line in lines)
1435
    p.Dedent()
1436
    p.Print("}")
1437

    
1438
  def _FormatLine(self, line):
1439
    return FormatDisasmLine(self.entry, self.heap, line)
1440

    
1441

    
1442
class V8Heap(object):
1443
  CLASS_MAP = {
1444
    "SYMBOL_TYPE": SeqString,
1445
    "ASCII_SYMBOL_TYPE": SeqString,
1446
    "CONS_SYMBOL_TYPE": ConsString,
1447
    "CONS_ASCII_SYMBOL_TYPE": ConsString,
1448
    "EXTERNAL_SYMBOL_TYPE": ExternalString,
1449
    "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
1450
    "EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
1451
    "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString,
1452
    "SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
1453
    "SHORT_EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
1454
    "STRING_TYPE": SeqString,
1455
    "ASCII_STRING_TYPE": SeqString,
1456
    "CONS_STRING_TYPE": ConsString,
1457
    "CONS_ASCII_STRING_TYPE": ConsString,
1458
    "EXTERNAL_STRING_TYPE": ExternalString,
1459
    "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE": ExternalString,
1460
    "EXTERNAL_ASCII_STRING_TYPE": ExternalString,
1461
    "MAP_TYPE": Map,
1462
    "ODDBALL_TYPE": Oddball,
1463
    "FIXED_ARRAY_TYPE": FixedArray,
1464
    "JS_FUNCTION_TYPE": JSFunction,
1465
    "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
1466
    "SCRIPT_TYPE": Script,
1467
    "CODE_CACHE_TYPE": CodeCache,
1468
    "CODE_TYPE": Code,
1469
  }
1470

    
1471
  def __init__(self, reader, stack_map):
1472
    self.reader = reader
1473
    self.stack_map = stack_map
1474
    self.objects = {}
1475

    
1476
  def FindObjectOrSmi(self, tagged_address):
1477
    if (tagged_address & 1) == 0: return tagged_address / 2
1478
    return self.FindObject(tagged_address)
1479

    
1480
  def FindObject(self, tagged_address):
1481
    if tagged_address in self.objects:
1482
      return self.objects[tagged_address]
1483
    if (tagged_address & self.ObjectAlignmentMask()) != 1: return None
1484
    address = tagged_address - 1
1485
    if not self.reader.IsValidAddress(address): return None
1486
    map_tagged_address = self.reader.ReadUIntPtr(address)
1487
    if tagged_address == map_tagged_address:
1488
      # Meta map?
1489
      meta_map = Map(self, None, address)
1490
      instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
1491
      if instance_type_name != "MAP_TYPE": return None
1492
      meta_map.map = meta_map
1493
      object = meta_map
1494
    else:
1495
      map = self.FindMap(map_tagged_address)
1496
      if map is None: return None
1497
      instance_type_name = INSTANCE_TYPES.get(map.instance_type)
1498
      if instance_type_name is None: return None
1499
      cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
1500
      object = cls(self, map, address)
1501
    self.objects[tagged_address] = object
1502
    return object
1503

    
1504
  def FindMap(self, tagged_address):
1505
    if (tagged_address & self.MapAlignmentMask()) != 1: return None
1506
    address = tagged_address - 1
1507
    if not self.reader.IsValidAddress(address): return None
1508
    object = Map(self, None, address)
1509
    return object
1510

    
1511
  def IntSize(self):
1512
    return 4
1513

    
1514
  def PointerSize(self):
1515
    return self.reader.PointerSize()
1516

    
1517
  def ObjectAlignmentMask(self):
1518
    return self.PointerSize() - 1
1519

    
1520
  def MapAlignmentMask(self):
1521
    if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64:
1522
      return (1 << 4) - 1
1523
    elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM:
1524
      return (1 << 4) - 1
1525
    elif self.reader.arch == MD_CPU_ARCHITECTURE_X86:
1526
      return (1 << 5) - 1
1527

    
1528
  def PageAlignmentMask(self):
1529
    return (1 << 20) - 1
1530

    
1531

    
1532
class KnownObject(HeapObject):
1533
  def __init__(self, heap, known_name):
1534
    HeapObject.__init__(self, heap, None, None)
1535
    self.known_name = known_name
1536

    
1537
  def __str__(self):
1538
    return "<%s>" % self.known_name
1539

    
1540

    
1541
class KnownMap(HeapObject):
1542
  def __init__(self, heap, known_name, instance_type):
1543
    HeapObject.__init__(self, heap, None, None)
1544
    self.instance_type = instance_type
1545
    self.known_name = known_name
1546

    
1547
  def __str__(self):
1548
    return "<%s>" % self.known_name
1549

    
1550

    
1551
class InspectionPadawan(object):
1552
  """The padawan can improve annotations by sensing well-known objects."""
1553
  def __init__(self, reader, heap):
1554
    self.reader = reader
1555
    self.heap = heap
1556
    self.known_first_map_page = 0
1557
    self.known_first_data_page = 0
1558
    self.known_first_pointer_page = 0
1559

    
1560
  def __getattr__(self, name):
1561
    """An InspectionPadawan can be used instead of V8Heap, even though
1562
       it does not inherit from V8Heap (aka. mixin)."""
1563
    return getattr(self.heap, name)
1564

    
1565
  def GetPageOffset(self, tagged_address):
1566
    return tagged_address & self.heap.PageAlignmentMask()
1567

    
1568
  def IsInKnownMapSpace(self, tagged_address):
1569
    page_address = tagged_address & ~self.heap.PageAlignmentMask()
1570
    return page_address == self.known_first_map_page
1571

    
1572
  def IsInKnownOldSpace(self, tagged_address):
1573
    page_address = tagged_address & ~self.heap.PageAlignmentMask()
1574
    return page_address in [self.known_first_data_page,
1575
                            self.known_first_pointer_page]
1576

    
1577
  def ContainingKnownOldSpaceName(self, tagged_address):
1578
    page_address = tagged_address & ~self.heap.PageAlignmentMask()
1579
    if page_address == self.known_first_data_page: return "OLD_DATA_SPACE"
1580
    if page_address == self.known_first_pointer_page: return "OLD_POINTER_SPACE"
1581
    return None
1582

    
1583
  def SenseObject(self, tagged_address):
1584
    if self.IsInKnownOldSpace(tagged_address):
1585
      offset = self.GetPageOffset(tagged_address)
1586
      lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset)
1587
      known_obj_name = KNOWN_OBJECTS.get(lookup_key)
1588
      if known_obj_name:
1589
        return KnownObject(self, known_obj_name)
1590
    if self.IsInKnownMapSpace(tagged_address):
1591
      known_map = self.SenseMap(tagged_address)
1592
      if known_map:
1593
        return known_map
1594
    found_obj = self.heap.FindObject(tagged_address)
1595
    if found_obj: return found_obj
1596
    address = tagged_address - 1
1597
    if self.reader.IsValidAddress(address):
1598
      map_tagged_address = self.reader.ReadUIntPtr(address)
1599
      map = self.SenseMap(map_tagged_address)
1600
      if map is None: return None
1601
      instance_type_name = INSTANCE_TYPES.get(map.instance_type)
1602
      if instance_type_name is None: return None
1603
      cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
1604
      return cls(self, map, address)
1605
    return None
1606

    
1607
  def SenseMap(self, tagged_address):
1608
    if self.IsInKnownMapSpace(tagged_address):
1609
      offset = self.GetPageOffset(tagged_address)
1610
      known_map_info = KNOWN_MAPS.get(offset)
1611
      if known_map_info:
1612
        known_map_type, known_map_name = known_map_info
1613
        return KnownMap(self, known_map_name, known_map_type)
1614
    found_map = self.heap.FindMap(tagged_address)
1615
    if found_map: return found_map
1616
    return None
1617

    
1618
  def FindObjectOrSmi(self, tagged_address):
1619
    """When used as a mixin in place of V8Heap."""
1620
    found_obj = self.SenseObject(tagged_address)
1621
    if found_obj: return found_obj
1622
    if (tagged_address & 1) == 0:
1623
      return "Smi(%d)" % (tagged_address / 2)
1624
    else:
1625
      return "Unknown(%s)" % self.reader.FormatIntPtr(tagged_address)
1626

    
1627
  def FindObject(self, tagged_address):
1628
    """When used as a mixin in place of V8Heap."""
1629
    raise NotImplementedError
1630

    
1631
  def FindMap(self, tagged_address):
1632
    """When used as a mixin in place of V8Heap."""
1633
    raise NotImplementedError
1634

    
1635
  def PrintKnowledge(self):
1636
    print "  known_first_map_page = %s\n"\
1637
          "  known_first_data_page = %s\n"\
1638
          "  known_first_pointer_page = %s" % (
1639
          self.reader.FormatIntPtr(self.known_first_map_page),
1640
          self.reader.FormatIntPtr(self.known_first_data_page),
1641
          self.reader.FormatIntPtr(self.known_first_pointer_page))
1642

    
1643

    
1644
class InspectionShell(cmd.Cmd):
1645
  def __init__(self, reader, heap):
1646
    cmd.Cmd.__init__(self)
1647
    self.reader = reader
1648
    self.heap = heap
1649
    self.padawan = InspectionPadawan(reader, heap)
1650
    self.prompt = "(grok) "
1651

    
1652
  def do_da(self, address):
1653
    """
1654
     Print ASCII string starting at specified address.
1655
    """
1656
    address = int(address, 16)
1657
    string = ""
1658
    while self.reader.IsValidAddress(address):
1659
      code = self.reader.ReadU8(address)
1660
      if code < 128:
1661
        string += chr(code)
1662
      else:
1663
        break
1664
      address += 1
1665
    if string == "":
1666
      print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address)
1667
    else:
1668
      print "%s\n" % string
1669

    
1670
  def do_dd(self, address):
1671
    """
1672
     Interpret memory at the given address (if available) as a sequence
1673
     of words. Automatic alignment is not performed.
1674
    """
1675
    start = int(address, 16)
1676
    if (start & self.heap.ObjectAlignmentMask()) != 0:
1677
      print "Warning: Dumping un-aligned memory, is this what you had in mind?"
1678
    for slot in xrange(start,
1679
                       start + self.reader.PointerSize() * 10,
1680
                       self.reader.PointerSize()):
1681
      if not self.reader.IsValidAddress(slot):
1682
        print "Address is not contained within the minidump!"
1683
        return
1684
      maybe_address = self.reader.ReadUIntPtr(slot)
1685
      heap_object = self.padawan.SenseObject(maybe_address)
1686
      print "%s: %s %s" % (self.reader.FormatIntPtr(slot),
1687
                           self.reader.FormatIntPtr(maybe_address),
1688
                           heap_object or '')
1689

    
1690
  def do_do(self, address):
1691
    """
1692
     Interpret memory at the given address as a V8 object. Automatic
1693
     alignment makes sure that you can pass tagged as well as un-tagged
1694
     addresses.
1695
    """
1696
    address = int(address, 16)
1697
    if (address & self.heap.ObjectAlignmentMask()) == 0:
1698
      address = address + 1
1699
    elif (address & self.heap.ObjectAlignmentMask()) != 1:
1700
      print "Address doesn't look like a valid pointer!"
1701
      return
1702
    heap_object = self.padawan.SenseObject(address)
1703
    if heap_object:
1704
      heap_object.Print(Printer())
1705
    else:
1706
      print "Address cannot be interpreted as object!"
1707

    
1708
  def do_do_desc(self, address):
1709
    """
1710
      Print a descriptor array in a readable format.
1711
    """
1712
    start = int(address, 16)
1713
    if ((start & 1) == 1): start = start - 1
1714
    DescriptorArray(FixedArray(self.heap, None, start)).Print(Printer())
1715

    
1716
  def do_do_map(self, address):
1717
    """
1718
      Print a descriptor array in a readable format.
1719
    """
1720
    start = int(address, 16)
1721
    if ((start & 1) == 1): start = start - 1
1722
    Map(self.heap, None, start).Print(Printer())
1723

    
1724
  def do_do_trans(self, address):
1725
    """
1726
      Print a transition array in a readable format.
1727
    """
1728
    start = int(address, 16)
1729
    if ((start & 1) == 1): start = start - 1
1730
    TransitionArray(FixedArray(self.heap, None, start)).Print(Printer())
1731

    
1732
  def do_dp(self, address):
1733
    """
1734
     Interpret memory at the given address as being on a V8 heap page
1735
     and print information about the page header (if available).
1736
    """
1737
    address = int(address, 16)
1738
    page_address = address & ~self.heap.PageAlignmentMask()
1739
    if self.reader.IsValidAddress(page_address):
1740
      raise NotImplementedError
1741
    else:
1742
      print "Page header is not available!"
1743

    
1744
  def do_k(self, arguments):
1745
    """
1746
     Teach V8 heap layout information to the inspector. This increases
1747
     the amount of annotations the inspector can produce while dumping
1748
     data. The first page of each heap space is of particular interest
1749
     because it contains known objects that do not move.
1750
    """
1751
    self.padawan.PrintKnowledge()
1752

    
1753
  def do_kd(self, address):
1754
    """
1755
     Teach V8 heap layout information to the inspector. Set the first
1756
     data-space page by passing any pointer into that page.
1757
    """
1758
    address = int(address, 16)
1759
    page_address = address & ~self.heap.PageAlignmentMask()
1760
    self.padawan.known_first_data_page = page_address
1761

    
1762
  def do_km(self, address):
1763
    """
1764
     Teach V8 heap layout information to the inspector. Set the first
1765
     map-space page by passing any pointer into that page.
1766
    """
1767
    address = int(address, 16)
1768
    page_address = address & ~self.heap.PageAlignmentMask()
1769
    self.padawan.known_first_map_page = page_address
1770

    
1771
  def do_kp(self, address):
1772
    """
1773
     Teach V8 heap layout information to the inspector. Set the first
1774
     pointer-space page by passing any pointer into that page.
1775
    """
1776
    address = int(address, 16)
1777
    page_address = address & ~self.heap.PageAlignmentMask()
1778
    self.padawan.known_first_pointer_page = page_address
1779

    
1780
  def do_list(self, smth):
1781
    """
1782
     List all available memory regions.
1783
    """
1784
    def print_region(reader, start, size, location):
1785
      print "  %s - %s (%d bytes)" % (reader.FormatIntPtr(start),
1786
                                      reader.FormatIntPtr(start + size),
1787
                                      size)
1788
    print "Available memory regions:"
1789
    self.reader.ForEachMemoryRegion(print_region)
1790

    
1791
  def do_lm(self, arg):
1792
    """
1793
     List details for all loaded modules in the minidump. An argument can
1794
     be passed to limit the output to only those modules that contain the
1795
     argument as a substring (case insensitive match).
1796
    """
1797
    for module in self.reader.module_list.modules:
1798
      if arg:
1799
        name = GetModuleName(self.reader, module).lower()
1800
        if name.find(arg.lower()) >= 0:
1801
          PrintModuleDetails(self.reader, module)
1802
      else:
1803
        PrintModuleDetails(self.reader, module)
1804
    print
1805

    
1806
  def do_s(self, word):
1807
    """
1808
     Search for a given word in available memory regions. The given word
1809
     is expanded to full pointer size and searched at aligned as well as
1810
     un-aligned memory locations. Use 'sa' to search aligned locations
1811
     only.
1812
    """
1813
    try:
1814
      word = int(word, 0)
1815
    except ValueError:
1816
      print "Malformed word, prefix with '0x' to use hexadecimal format."
1817
      return
1818
    print "Searching for word %d/0x%s:" % (word, self.reader.FormatIntPtr(word))
1819
    self.reader.FindWord(word)
1820

    
1821
  def do_sh(self, none):
1822
    """
1823
     Search for the V8 Heap object in all available memory regions. You
1824
     might get lucky and find this rare treasure full of invaluable
1825
     information.
1826
    """
1827
    raise NotImplementedError
1828

    
1829
  def do_u(self, args):
1830
    """
1831
     Unassemble memory in the region [address, address + size). If the
1832
     size is not specified, a default value of 32 bytes is used.
1833
     Synopsis: u 0x<address> 0x<size>
1834
    """
1835
    args = args.split(' ')
1836
    start = int(args[0], 16)
1837
    size = int(args[1], 16) if len(args) > 1 else 0x20
1838
    if not self.reader.IsValidAddress(start):
1839
      print "Address is not contained within the minidump!"
1840
      return
1841
    lines = self.reader.GetDisasmLines(start, size)
1842
    for line in lines:
1843
      print FormatDisasmLine(start, self.heap, line)
1844
    print
1845

    
1846
  def do_EOF(self, none):
1847
    raise KeyboardInterrupt
1848

    
1849
EIP_PROXIMITY = 64
1850

    
1851
CONTEXT_FOR_ARCH = {
1852
    MD_CPU_ARCHITECTURE_AMD64:
1853
      ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip',
1854
       'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'],
1855
    MD_CPU_ARCHITECTURE_ARM:
1856
      ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
1857
       'r10', 'r11', 'r12', 'sp', 'lr', 'pc'],
1858
    MD_CPU_ARCHITECTURE_X86:
1859
      ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
1860
}
1861

    
1862
KNOWN_MODULES = {'chrome.exe', 'chrome.dll'}
1863

    
1864
def GetVersionString(ms, ls):
1865
  return "%d.%d.%d.%d" % (ms >> 16, ms & 0xffff, ls >> 16, ls & 0xffff)
1866

    
1867

    
1868
def GetModuleName(reader, module):
1869
  name = reader.ReadMinidumpString(module.module_name_rva)
1870
  # simplify for path manipulation
1871
  name = name.encode('utf-8')
1872
  return str(os.path.basename(str(name).replace("\\", "/")))
1873

    
1874

    
1875
def PrintModuleDetails(reader, module):
1876
  print "%s" % GetModuleName(reader, module)
1877
  file_version = GetVersionString(module.version_info.dwFileVersionMS,
1878
                                  module.version_info.dwFileVersionLS)
1879
  product_version = GetVersionString(module.version_info.dwProductVersionMS,
1880
                                     module.version_info.dwProductVersionLS)
1881
  print "  base: %s" % reader.FormatIntPtr(module.base_of_image)
1882
  print "  end: %s" % reader.FormatIntPtr(module.base_of_image +
1883
                                          module.size_of_image)
1884
  print "  file version: %s" % file_version
1885
  print "  product version: %s" % product_version
1886
  time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
1887
  print "  timestamp: %s" % time_date_stamp
1888

    
1889

    
1890
def AnalyzeMinidump(options, minidump_name):
1891
  reader = MinidumpReader(options, minidump_name)
1892
  heap = None
1893
  DebugPrint("========================================")
1894
  if reader.exception is None:
1895
    print "Minidump has no exception info"
1896
  else:
1897
    print "Exception info:"
1898
    exception_thread = reader.thread_map[reader.exception.thread_id]
1899
    print "  thread id: %d" % exception_thread.id
1900
    print "  code: %08X" % reader.exception.exception.code
1901
    print "  context:"
1902
    for r in CONTEXT_FOR_ARCH[reader.arch]:
1903
      print "    %s: %s" % (r, reader.FormatIntPtr(reader.Register(r)))
1904
    # TODO(vitalyr): decode eflags.
1905
    if reader.arch == MD_CPU_ARCHITECTURE_ARM:
1906
      print "    cpsr: %s" % bin(reader.exception_context.cpsr)[2:]
1907
    else:
1908
      print "    eflags: %s" % bin(reader.exception_context.eflags)[2:]
1909

    
1910
    print
1911
    print "  modules:"
1912
    for module in reader.module_list.modules:
1913
      name = GetModuleName(reader, module)
1914
      if name in KNOWN_MODULES:
1915
        print "    %s at %08X" % (name, module.base_of_image)
1916
        reader.TryLoadSymbolsFor(name, module)
1917
    print
1918

    
1919
    stack_top = reader.ExceptionSP()
1920
    stack_bottom = exception_thread.stack.start + \
1921
        exception_thread.stack.memory.data_size
1922
    stack_map = {reader.ExceptionIP(): -1}
1923
    for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
1924
      maybe_address = reader.ReadUIntPtr(slot)
1925
      if not maybe_address in stack_map:
1926
        stack_map[maybe_address] = slot
1927
    heap = V8Heap(reader, stack_map)
1928

    
1929
    print "Disassembly around exception.eip:"
1930
    eip_symbol = reader.FindSymbol(reader.ExceptionIP())
1931
    if eip_symbol is not None:
1932
      print eip_symbol
1933
    disasm_start = reader.ExceptionIP() - EIP_PROXIMITY
1934
    disasm_bytes = 2 * EIP_PROXIMITY
1935
    if (options.full):
1936
      full_range = reader.FindRegion(reader.ExceptionIP())
1937
      if full_range is not None:
1938
        disasm_start = full_range[0]
1939
        disasm_bytes = full_range[1]
1940

    
1941
    lines = reader.GetDisasmLines(disasm_start, disasm_bytes)
1942

    
1943
    for line in lines:
1944
      print FormatDisasmLine(disasm_start, heap, line)
1945
    print
1946

    
1947
  if heap is None:
1948
    heap = V8Heap(reader, None)
1949

    
1950
  if options.full:
1951
    FullDump(reader, heap)
1952

    
1953
  if options.command:
1954
    InspectionShell(reader, heap).onecmd(options.command)
1955

    
1956
  if options.shell:
1957
    try:
1958
      InspectionShell(reader, heap).cmdloop("type help to get help")
1959
    except KeyboardInterrupt:
1960
      print "Kthxbye."
1961
  elif not options.command:
1962
    if reader.exception is not None:
1963
      frame_pointer = reader.ExceptionFP()
1964
      print "Annotated stack (from exception.esp to bottom):"
1965
      for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
1966
        maybe_address = reader.ReadUIntPtr(slot)
1967
        heap_object = heap.FindObject(maybe_address)
1968
        maybe_symbol = reader.FindSymbol(maybe_address)
1969
        if slot == frame_pointer:
1970
          maybe_symbol = "<---- frame pointer"
1971
          frame_pointer = maybe_address
1972
        print "%s: %s %s" % (reader.FormatIntPtr(slot),
1973
                             reader.FormatIntPtr(maybe_address),
1974
                             maybe_symbol or "")
1975
        if heap_object:
1976
          heap_object.Print(Printer())
1977
          print
1978

    
1979
  reader.Dispose()
1980

    
1981

    
1982
if __name__ == "__main__":
1983
  parser = optparse.OptionParser(USAGE)
1984
  parser.add_option("-s", "--shell", dest="shell", action="store_true",
1985
                    help="start an interactive inspector shell")
1986
  parser.add_option("-c", "--command", dest="command", default="",
1987
                    help="run an interactive inspector shell command and exit")
1988
  parser.add_option("-f", "--full", dest="full", action="store_true",
1989
                    help="dump all information contained in the minidump")
1990
  parser.add_option("--symdir", dest="symdir", default=".",
1991
                    help="directory containing *.pdb.sym file with symbols")
1992
  parser.add_option("--objdump",
1993
                    default="/usr/bin/objdump",
1994
                    help="objdump tool to use [default: %default]")
1995
  options, args = parser.parse_args()
1996
  if os.path.exists(options.objdump):
1997
    disasm.OBJDUMP_BIN = options.objdump
1998
    OBJDUMP_BIN = options.objdump
1999
  else:
2000
    print "Cannot find %s, falling back to default objdump" % options.objdump
2001
  if len(args) != 1:
2002
    parser.print_help()
2003
    sys.exit(1)
2004
  AnalyzeMinidump(options, args[0])