blob: ab8f3265a6efe7fe870d82a3f1c404ebd0b45042 [file] [log] [blame]
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001#!/usr/bin/env python
2#
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003# Copyright 2012 the V8 project authors. All rights reserved.
Ben Murdoche0cee9b2011-05-25 10:26:03 +01004# 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
Ben Murdochb8a8cc12014-11-26 15:28:44 +000030import BaseHTTPServer
31import bisect
32import cgi
33import cmd
34import codecs
Ben Murdoche0cee9b2011-05-25 10:26:03 +010035import ctypes
Ben Murdochb8a8cc12014-11-26 15:28:44 +000036import datetime
37import disasm
Ben Murdoche0cee9b2011-05-25 10:26:03 +010038import mmap
39import optparse
40import os
Ben Murdochb8a8cc12014-11-26 15:28:44 +000041import re
Ben Murdoche0cee9b2011-05-25 10:26:03 +010042import sys
43import types
Ben Murdochb8a8cc12014-11-26 15:28:44 +000044import urllib
45import urlparse
46import v8heapconst
47import webbrowser
48
49PORT_NUMBER = 8081
Ben Murdoche0cee9b2011-05-25 10:26:03 +010050
51
Ben Murdochb8a8cc12014-11-26 15:28:44 +000052USAGE="""usage: %prog [OPTIONS] [DUMP-FILE]
Ben Murdoche0cee9b2011-05-25 10:26:03 +010053
54Minidump analyzer.
55
56Shows the processor state at the point of exception including the
57stack of the active thread and the referenced objects in the V8
58heap. Code objects are disassembled and the addresses linked from the
Ben Murdochb8a8cc12014-11-26 15:28:44 +000059stack (e.g. pushed return addresses) are marked with "=>".
Ben Murdoche0cee9b2011-05-25 10:26:03 +010060
61Examples:
Ben Murdochb8a8cc12014-11-26 15:28:44 +000062 $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp"""
Ben Murdoche0cee9b2011-05-25 10:26:03 +010063
Ben Murdoch3ef787d2012-04-12 10:51:47 +010064
Ben Murdoche0cee9b2011-05-25 10:26:03 +010065DEBUG=False
66
67
68def DebugPrint(s):
69 if not DEBUG: return
70 print s
71
72
73class Descriptor(object):
74 """Descriptor of a structure in a memory."""
75
76 def __init__(self, fields):
77 self.fields = fields
78 self.is_flexible = False
79 for _, type_or_func in fields:
80 if isinstance(type_or_func, types.FunctionType):
81 self.is_flexible = True
82 break
83 if not self.is_flexible:
84 self.ctype = Descriptor._GetCtype(fields)
85 self.size = ctypes.sizeof(self.ctype)
86
87 def Read(self, memory, offset):
88 if self.is_flexible:
89 fields_copy = self.fields[:]
90 last = 0
91 for name, type_or_func in fields_copy:
92 if isinstance(type_or_func, types.FunctionType):
93 partial_ctype = Descriptor._GetCtype(fields_copy[:last])
94 partial_object = partial_ctype.from_buffer(memory, offset)
95 type = type_or_func(partial_object)
96 if type is not None:
97 fields_copy[last] = (name, type)
98 last += 1
99 else:
100 last += 1
101 complete_ctype = Descriptor._GetCtype(fields_copy[:last])
102 else:
103 complete_ctype = self.ctype
104 return complete_ctype.from_buffer(memory, offset)
105
106 @staticmethod
107 def _GetCtype(fields):
108 class Raw(ctypes.Structure):
109 _fields_ = fields
110 _pack_ = 1
111
112 def __str__(self):
113 return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field))
114 for field, _ in Raw._fields_) + "}"
115 return Raw
116
117
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000118def FullDump(reader, heap):
119 """Dump all available memory regions."""
120 def dump_region(reader, start, size, location):
121 print
122 while start & 3 != 0:
123 start += 1
124 size -= 1
125 location += 1
126 is_executable = reader.IsProbableExecutableRegion(location, size)
127 is_ascii = reader.IsProbableASCIIRegion(location, size)
128
129 if is_executable is not False:
130 lines = reader.GetDisasmLines(start, size)
131 for line in lines:
132 print FormatDisasmLine(start, heap, line)
133 print
134
135 if is_ascii is not False:
136 # Output in the same format as the Unix hd command
137 addr = start
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000138 for i in xrange(0, size, 16):
139 slot = i + location
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000140 hex_line = ""
141 asc_line = ""
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000142 for i in xrange(16):
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000143 if slot + i < location + size:
144 byte = ctypes.c_uint8.from_buffer(reader.minidump, slot + i).value
145 if byte >= 0x20 and byte < 0x7f:
146 asc_line += chr(byte)
147 else:
148 asc_line += "."
149 hex_line += " %02x" % (byte)
150 else:
151 hex_line += " "
152 if i == 7:
153 hex_line += " "
154 print "%s %s |%s|" % (reader.FormatIntPtr(addr),
155 hex_line,
156 asc_line)
157 addr += 16
158
159 if is_executable is not True and is_ascii is not True:
160 print "%s - %s" % (reader.FormatIntPtr(start),
161 reader.FormatIntPtr(start + size))
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000162 print start + size + 1;
163 for i in xrange(0, size, reader.PointerSize()):
164 slot = start + i
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000165 maybe_address = reader.ReadUIntPtr(slot)
166 heap_object = heap.FindObject(maybe_address)
167 print "%s: %s" % (reader.FormatIntPtr(slot),
168 reader.FormatIntPtr(maybe_address))
169 if heap_object:
170 heap_object.Print(Printer())
171 print
172
173 reader.ForEachMemoryRegion(dump_region)
174
175# Heap constants generated by 'make grokdump' in v8heapconst module.
176INSTANCE_TYPES = v8heapconst.INSTANCE_TYPES
177KNOWN_MAPS = v8heapconst.KNOWN_MAPS
178KNOWN_OBJECTS = v8heapconst.KNOWN_OBJECTS
179
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100180# Set of structures and constants that describe the layout of minidump
181# files. Based on MSDN and Google Breakpad.
182
183MINIDUMP_HEADER = Descriptor([
184 ("signature", ctypes.c_uint32),
185 ("version", ctypes.c_uint32),
186 ("stream_count", ctypes.c_uint32),
187 ("stream_directories_rva", ctypes.c_uint32),
188 ("checksum", ctypes.c_uint32),
189 ("time_date_stampt", ctypes.c_uint32),
190 ("flags", ctypes.c_uint64)
191])
192
193MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
194 ("data_size", ctypes.c_uint32),
195 ("rva", ctypes.c_uint32)
196])
197
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000198MINIDUMP_STRING = Descriptor([
199 ("length", ctypes.c_uint32),
200 ("buffer", lambda t: ctypes.c_uint8 * (t.length + 2))
201])
202
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100203MINIDUMP_DIRECTORY = Descriptor([
204 ("stream_type", ctypes.c_uint32),
205 ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
206])
207
208MD_EXCEPTION_MAXIMUM_PARAMETERS = 15
209
210MINIDUMP_EXCEPTION = Descriptor([
211 ("code", ctypes.c_uint32),
212 ("flags", ctypes.c_uint32),
213 ("record", ctypes.c_uint64),
214 ("address", ctypes.c_uint64),
215 ("parameter_count", ctypes.c_uint32),
216 ("unused_alignment", ctypes.c_uint32),
217 ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS)
218])
219
220MINIDUMP_EXCEPTION_STREAM = Descriptor([
221 ("thread_id", ctypes.c_uint32),
222 ("unused_alignment", ctypes.c_uint32),
223 ("exception", MINIDUMP_EXCEPTION.ctype),
224 ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
225])
226
227# Stream types.
228MD_UNUSED_STREAM = 0
229MD_RESERVED_STREAM_0 = 1
230MD_RESERVED_STREAM_1 = 2
231MD_THREAD_LIST_STREAM = 3
232MD_MODULE_LIST_STREAM = 4
233MD_MEMORY_LIST_STREAM = 5
234MD_EXCEPTION_STREAM = 6
235MD_SYSTEM_INFO_STREAM = 7
236MD_THREAD_EX_LIST_STREAM = 8
237MD_MEMORY_64_LIST_STREAM = 9
238MD_COMMENT_STREAM_A = 10
239MD_COMMENT_STREAM_W = 11
240MD_HANDLE_DATA_STREAM = 12
241MD_FUNCTION_TABLE_STREAM = 13
242MD_UNLOADED_MODULE_LIST_STREAM = 14
243MD_MISC_INFO_STREAM = 15
244MD_MEMORY_INFO_LIST_STREAM = 16
245MD_THREAD_INFO_LIST_STREAM = 17
246MD_HANDLE_OPERATION_LIST_STREAM = 18
247
248MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80
249
250MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([
251 ("control_word", ctypes.c_uint32),
252 ("status_word", ctypes.c_uint32),
253 ("tag_word", ctypes.c_uint32),
254 ("error_offset", ctypes.c_uint32),
255 ("error_selector", ctypes.c_uint32),
256 ("data_offset", ctypes.c_uint32),
257 ("data_selector", ctypes.c_uint32),
258 ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE),
259 ("cr0_npx_state", ctypes.c_uint32)
260])
261
262MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512
263
264# Context flags.
265MD_CONTEXT_X86 = 0x00010000
266MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001)
267MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002)
268MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004)
269MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008)
270MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010)
271MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020)
272
273def EnableOnFlag(type, flag):
274 return lambda o: [None, type][int((o.context_flags & flag) != 0)]
275
276MINIDUMP_CONTEXT_X86 = Descriptor([
277 ("context_flags", ctypes.c_uint32),
278 # MD_CONTEXT_X86_DEBUG_REGISTERS.
279 ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
280 ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
281 ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
282 ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
283 ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
284 ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
285 # MD_CONTEXT_X86_FLOATING_POINT.
286 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype,
287 MD_CONTEXT_X86_FLOATING_POINT)),
288 # MD_CONTEXT_X86_SEGMENTS.
289 ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
290 ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
291 ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
292 ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
293 # MD_CONTEXT_X86_INTEGER.
294 ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
295 ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
296 ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
297 ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
298 ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
299 ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
300 # MD_CONTEXT_X86_CONTROL.
301 ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
302 ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
303 ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
304 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
305 ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
306 ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
307 # MD_CONTEXT_X86_EXTENDED_REGISTERS.
308 ("extended_registers",
309 EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE,
310 MD_CONTEXT_X86_EXTENDED_REGISTERS))
311])
312
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000313MD_CONTEXT_ARM = 0x40000000
314MD_CONTEXT_ARM_INTEGER = (MD_CONTEXT_ARM | 0x00000002)
315MD_CONTEXT_ARM_FLOATING_POINT = (MD_CONTEXT_ARM | 0x00000004)
316MD_FLOATINGSAVEAREA_ARM_FPR_COUNT = 32
317MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT = 8
318
319MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([
320 ("fpscr", ctypes.c_uint64),
321 ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPR_COUNT),
322 ("extra", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT)
323])
324
325MINIDUMP_CONTEXT_ARM = Descriptor([
326 ("context_flags", ctypes.c_uint32),
327 # MD_CONTEXT_ARM_INTEGER.
328 ("r0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
329 ("r1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
330 ("r2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
331 ("r3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
332 ("r4", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
333 ("r5", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
334 ("r6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
335 ("r7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
336 ("r8", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
337 ("r9", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
338 ("r10", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
339 ("r11", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
340 ("r12", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
341 ("sp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
342 ("lr", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
343 ("pc", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
344 ("cpsr", ctypes.c_uint32),
345 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype,
346 MD_CONTEXT_ARM_FLOATING_POINT))
347])
348
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000349
350MD_CONTEXT_ARM64 = 0x80000000
351MD_CONTEXT_ARM64_INTEGER = (MD_CONTEXT_ARM64 | 0x00000002)
352MD_CONTEXT_ARM64_FLOATING_POINT = (MD_CONTEXT_ARM64 | 0x00000004)
353MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT = 64
354
355MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([
356 ("fpscr", ctypes.c_uint64),
357 ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT),
358])
359
360MINIDUMP_CONTEXT_ARM64 = Descriptor([
361 ("context_flags", ctypes.c_uint64),
362 # MD_CONTEXT_ARM64_INTEGER.
363 ("r0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
364 ("r1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
365 ("r2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
366 ("r3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
367 ("r4", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
368 ("r5", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
369 ("r6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
370 ("r7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
371 ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
372 ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
373 ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
374 ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
375 ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
376 ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
377 ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
378 ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
379 ("r16", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
380 ("r17", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
381 ("r18", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
382 ("r19", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
383 ("r20", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
384 ("r21", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
385 ("r22", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
386 ("r23", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
387 ("r24", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
388 ("r25", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
389 ("r26", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
390 ("r27", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
391 ("r28", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
392 ("fp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
393 ("lr", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
394 ("sp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
395 ("pc", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
396 ("cpsr", ctypes.c_uint32),
397 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype,
398 MD_CONTEXT_ARM64_FLOATING_POINT))
399])
400
401
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100402MD_CONTEXT_AMD64 = 0x00100000
403MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001)
404MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002)
405MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004)
406MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008)
407MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010)
408
409MINIDUMP_CONTEXT_AMD64 = Descriptor([
410 ("p1_home", ctypes.c_uint64),
411 ("p2_home", ctypes.c_uint64),
412 ("p3_home", ctypes.c_uint64),
413 ("p4_home", ctypes.c_uint64),
414 ("p5_home", ctypes.c_uint64),
415 ("p6_home", ctypes.c_uint64),
416 ("context_flags", ctypes.c_uint32),
417 ("mx_csr", ctypes.c_uint32),
418 # MD_CONTEXT_AMD64_CONTROL.
419 ("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
420 # MD_CONTEXT_AMD64_SEGMENTS
421 ("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
422 ("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
423 ("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
424 ("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
425 # MD_CONTEXT_AMD64_CONTROL.
426 ("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
427 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)),
428 # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
429 ("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
430 ("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
431 ("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
432 ("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
433 ("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
434 ("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
435 # MD_CONTEXT_AMD64_INTEGER.
436 ("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
437 ("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
438 ("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
439 ("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
440 # MD_CONTEXT_AMD64_CONTROL.
441 ("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
442 # MD_CONTEXT_AMD64_INTEGER.
443 ("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
444 ("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
445 ("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
446 ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
447 ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
448 ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
449 ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
450 ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
451 ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
452 ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
453 ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
454 # MD_CONTEXT_AMD64_CONTROL.
455 ("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
456 # MD_CONTEXT_AMD64_FLOATING_POINT
457 ("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
458 MD_CONTEXT_AMD64_FLOATING_POINT)),
459 ("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
460 MD_CONTEXT_AMD64_FLOATING_POINT)),
461 ("vector_control", EnableOnFlag(ctypes.c_uint64,
462 MD_CONTEXT_AMD64_FLOATING_POINT)),
463 # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
464 ("debug_control", EnableOnFlag(ctypes.c_uint64,
465 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
466 ("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64,
467 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
468 ("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64,
469 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
470 ("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64,
471 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
472 ("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64,
473 MD_CONTEXT_AMD64_DEBUG_REGISTERS))
474])
475
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100476MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
477 ("start", ctypes.c_uint64),
478 ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
479])
480
481MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
482 ("start", ctypes.c_uint64),
483 ("size", ctypes.c_uint64)
484])
485
486MINIDUMP_MEMORY_LIST = Descriptor([
487 ("range_count", ctypes.c_uint32),
488 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
489])
490
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000491MINIDUMP_MEMORY_LIST_Mac = Descriptor([
492 ("range_count", ctypes.c_uint32),
493 ("junk", ctypes.c_uint32),
494 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
495])
496
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100497MINIDUMP_MEMORY_LIST64 = Descriptor([
498 ("range_count", ctypes.c_uint64),
499 ("base_rva", ctypes.c_uint64),
500 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count)
501])
502
503MINIDUMP_THREAD = Descriptor([
504 ("id", ctypes.c_uint32),
505 ("suspend_count", ctypes.c_uint32),
506 ("priority_class", ctypes.c_uint32),
507 ("priority", ctypes.c_uint32),
508 ("ted", ctypes.c_uint64),
509 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype),
510 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
511])
512
513MINIDUMP_THREAD_LIST = Descriptor([
514 ("thread_count", ctypes.c_uint32),
515 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
516])
517
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000518MINIDUMP_THREAD_LIST_Mac = Descriptor([
519 ("thread_count", ctypes.c_uint32),
520 ("junk", ctypes.c_uint32),
521 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
522])
523
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000524MINIDUMP_VS_FIXEDFILEINFO = Descriptor([
525 ("dwSignature", ctypes.c_uint32),
526 ("dwStrucVersion", ctypes.c_uint32),
527 ("dwFileVersionMS", ctypes.c_uint32),
528 ("dwFileVersionLS", ctypes.c_uint32),
529 ("dwProductVersionMS", ctypes.c_uint32),
530 ("dwProductVersionLS", ctypes.c_uint32),
531 ("dwFileFlagsMask", ctypes.c_uint32),
532 ("dwFileFlags", ctypes.c_uint32),
533 ("dwFileOS", ctypes.c_uint32),
534 ("dwFileType", ctypes.c_uint32),
535 ("dwFileSubtype", ctypes.c_uint32),
536 ("dwFileDateMS", ctypes.c_uint32),
537 ("dwFileDateLS", ctypes.c_uint32)
538])
539
540MINIDUMP_RAW_MODULE = Descriptor([
541 ("base_of_image", ctypes.c_uint64),
542 ("size_of_image", ctypes.c_uint32),
543 ("checksum", ctypes.c_uint32),
544 ("time_date_stamp", ctypes.c_uint32),
545 ("module_name_rva", ctypes.c_uint32),
546 ("version_info", MINIDUMP_VS_FIXEDFILEINFO.ctype),
547 ("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
548 ("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
549 ("reserved0", ctypes.c_uint32 * 2),
550 ("reserved1", ctypes.c_uint32 * 2)
551])
552
553MINIDUMP_MODULE_LIST = Descriptor([
554 ("number_of_modules", ctypes.c_uint32),
555 ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules)
556])
557
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000558MINIDUMP_MODULE_LIST_Mac = Descriptor([
559 ("number_of_modules", ctypes.c_uint32),
560 ("junk", ctypes.c_uint32),
561 ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules)
562])
563
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100564MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
565 ("processor_architecture", ctypes.c_uint16)
566])
567
568MD_CPU_ARCHITECTURE_X86 = 0
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000569MD_CPU_ARCHITECTURE_ARM = 5
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000570MD_CPU_ARCHITECTURE_ARM64 = 0x8003
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100571MD_CPU_ARCHITECTURE_AMD64 = 9
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100572
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000573class FuncSymbol:
574 def __init__(self, start, size, name):
575 self.start = start
576 self.end = self.start + size
577 self.name = name
578
579 def __cmp__(self, other):
580 if isinstance(other, FuncSymbol):
581 return self.start - other.start
582 return self.start - other
583
584 def Covers(self, addr):
585 return (self.start <= addr) and (addr < self.end)
586
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100587class MinidumpReader(object):
588 """Minidump (.dmp) reader."""
589
590 _HEADER_MAGIC = 0x504d444d
591
592 def __init__(self, options, minidump_name):
593 self.minidump_name = minidump_name
594 self.minidump_file = open(minidump_name, "r")
595 self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE)
596 self.header = MINIDUMP_HEADER.Read(self.minidump, 0)
597 if self.header.signature != MinidumpReader._HEADER_MAGIC:
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000598 print >>sys.stderr, "Warning: Unsupported minidump header magic!"
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100599 DebugPrint(self.header)
600 directories = []
601 offset = self.header.stream_directories_rva
602 for _ in xrange(self.header.stream_count):
603 directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
604 offset += MINIDUMP_DIRECTORY.size
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100605 self.arch = None
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100606 self.exception = None
607 self.exception_context = None
608 self.memory_list = None
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000609 self.memory_list64 = None
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000610 self.module_list = None
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100611 self.thread_map = {}
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100612
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000613 self.symdir = options.symdir
614 self.modules_with_symbols = []
615 self.symbols = []
616
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100617 # Find MDRawSystemInfo stream and determine arch.
618 for d in directories:
619 if d.stream_type == MD_SYSTEM_INFO_STREAM:
620 system_info = MINIDUMP_RAW_SYSTEM_INFO.Read(
621 self.minidump, d.location.rva)
622 self.arch = system_info.processor_architecture
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000623 assert self.arch in [MD_CPU_ARCHITECTURE_AMD64,
624 MD_CPU_ARCHITECTURE_ARM,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000625 MD_CPU_ARCHITECTURE_ARM64,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000626 MD_CPU_ARCHITECTURE_X86]
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100627 assert not self.arch is None
628
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100629 for d in directories:
630 DebugPrint(d)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100631 if d.stream_type == MD_EXCEPTION_STREAM:
632 self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
633 self.minidump, d.location.rva)
634 DebugPrint(self.exception)
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100635 if self.arch == MD_CPU_ARCHITECTURE_X86:
636 self.exception_context = MINIDUMP_CONTEXT_X86.Read(
637 self.minidump, self.exception.thread_context.rva)
638 elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
639 self.exception_context = MINIDUMP_CONTEXT_AMD64.Read(
640 self.minidump, self.exception.thread_context.rva)
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000641 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
642 self.exception_context = MINIDUMP_CONTEXT_ARM.Read(
643 self.minidump, self.exception.thread_context.rva)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000644 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
645 self.exception_context = MINIDUMP_CONTEXT_ARM64.Read(
646 self.minidump, self.exception.thread_context.rva)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100647 DebugPrint(self.exception_context)
648 elif d.stream_type == MD_THREAD_LIST_STREAM:
649 thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000650 if ctypes.sizeof(thread_list) + 4 == d.location.data_size:
651 thread_list = MINIDUMP_THREAD_LIST_Mac.Read(
652 self.minidump, d.location.rva)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100653 assert ctypes.sizeof(thread_list) == d.location.data_size
654 DebugPrint(thread_list)
655 for thread in thread_list.threads:
656 DebugPrint(thread)
657 self.thread_map[thread.id] = thread
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000658 elif d.stream_type == MD_MODULE_LIST_STREAM:
659 assert self.module_list is None
660 self.module_list = MINIDUMP_MODULE_LIST.Read(
661 self.minidump, d.location.rva)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000662 if ctypes.sizeof(self.module_list) + 4 == d.location.data_size:
663 self.module_list = MINIDUMP_MODULE_LIST_Mac.Read(
664 self.minidump, d.location.rva)
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000665 assert ctypes.sizeof(self.module_list) == d.location.data_size
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000666 DebugPrint(self.module_list)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100667 elif d.stream_type == MD_MEMORY_LIST_STREAM:
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000668 print >>sys.stderr, "Warning: This is not a full minidump!"
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100669 assert self.memory_list is None
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000670 self.memory_list = MINIDUMP_MEMORY_LIST.Read(
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100671 self.minidump, d.location.rva)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000672 if ctypes.sizeof(self.memory_list) + 4 == d.location.data_size:
673 self.memory_list = MINIDUMP_MEMORY_LIST_Mac.Read(
674 self.minidump, d.location.rva)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100675 assert ctypes.sizeof(self.memory_list) == d.location.data_size
676 DebugPrint(self.memory_list)
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000677 elif d.stream_type == MD_MEMORY_64_LIST_STREAM:
678 assert self.memory_list64 is None
679 self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read(
680 self.minidump, d.location.rva)
681 assert ctypes.sizeof(self.memory_list64) == d.location.data_size
682 DebugPrint(self.memory_list64)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100683
684 def IsValidAddress(self, address):
685 return self.FindLocation(address) is not None
686
687 def ReadU8(self, address):
688 location = self.FindLocation(address)
689 return ctypes.c_uint8.from_buffer(self.minidump, location).value
690
691 def ReadU32(self, address):
692 location = self.FindLocation(address)
693 return ctypes.c_uint32.from_buffer(self.minidump, location).value
694
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100695 def ReadU64(self, address):
696 location = self.FindLocation(address)
697 return ctypes.c_uint64.from_buffer(self.minidump, location).value
698
699 def ReadUIntPtr(self, address):
700 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
701 return self.ReadU64(address)
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000702 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
703 return self.ReadU32(address)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000704 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
705 return self.ReadU64(address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100706 elif self.arch == MD_CPU_ARCHITECTURE_X86:
707 return self.ReadU32(address)
708
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100709 def ReadBytes(self, address, size):
710 location = self.FindLocation(address)
711 return self.minidump[location:location + size]
712
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000713 def _ReadWord(self, location):
714 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
715 return ctypes.c_uint64.from_buffer(self.minidump, location).value
716 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
717 return ctypes.c_uint32.from_buffer(self.minidump, location).value
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000718 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
719 return ctypes.c_uint64.from_buffer(self.minidump, location).value
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000720 elif self.arch == MD_CPU_ARCHITECTURE_X86:
721 return ctypes.c_uint32.from_buffer(self.minidump, location).value
722
723 def IsProbableASCIIRegion(self, location, length):
724 ascii_bytes = 0
725 non_ascii_bytes = 0
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000726 for i in xrange(length):
727 loc = location + i
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000728 byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
729 if byte >= 0x7f:
730 non_ascii_bytes += 1
731 if byte < 0x20 and byte != 0:
732 non_ascii_bytes += 1
733 if byte < 0x7f and byte >= 0x20:
734 ascii_bytes += 1
735 if byte == 0xa: # newline
736 ascii_bytes += 1
737 if ascii_bytes * 10 <= length:
738 return False
739 if length > 0 and ascii_bytes > non_ascii_bytes * 7:
740 return True
741 if ascii_bytes > non_ascii_bytes * 3:
742 return None # Maybe
743 return False
744
745 def IsProbableExecutableRegion(self, location, length):
746 opcode_bytes = 0
747 sixty_four = self.arch == MD_CPU_ARCHITECTURE_AMD64
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000748 for i in xrange(length):
749 loc = location + i
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000750 byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
751 if (byte == 0x8b or # mov
752 byte == 0x89 or # mov reg-reg
753 (byte & 0xf0) == 0x50 or # push/pop
754 (sixty_four and (byte & 0xf0) == 0x40) or # rex prefix
755 byte == 0xc3 or # return
756 byte == 0x74 or # jeq
757 byte == 0x84 or # jeq far
758 byte == 0x75 or # jne
759 byte == 0x85 or # jne far
760 byte == 0xe8 or # call
761 byte == 0xe9 or # jmp far
762 byte == 0xeb): # jmp near
763 opcode_bytes += 1
764 opcode_percent = (opcode_bytes * 100) / length
765 threshold = 20
766 if opcode_percent > threshold + 2:
767 return True
768 if opcode_percent > threshold - 2:
769 return None # Maybe
770 return False
771
772 def FindRegion(self, addr):
773 answer = [-1, -1]
774 def is_in(reader, start, size, location):
775 if addr >= start and addr < start + size:
776 answer[0] = start
777 answer[1] = size
778 self.ForEachMemoryRegion(is_in)
779 if answer[0] == -1:
780 return None
781 return answer
782
783 def ForEachMemoryRegion(self, cb):
784 if self.memory_list64 is not None:
785 for r in self.memory_list64.ranges:
786 location = self.memory_list64.base_rva + offset
787 cb(self, r.start, r.size, location)
788 offset += r.size
789
790 if self.memory_list is not None:
791 for r in self.memory_list.ranges:
792 cb(self, r.start, r.memory.data_size, r.memory.rva)
793
794 def FindWord(self, word, alignment=0):
795 def search_inside_region(reader, start, size, location):
796 location = (location + alignment) & ~alignment
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000797 for i in xrange(size - self.PointerSize()):
798 loc = location + i
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000799 if reader._ReadWord(loc) == word:
800 slot = start + (loc - location)
801 print "%s: %s" % (reader.FormatIntPtr(slot),
802 reader.FormatIntPtr(word))
803 self.ForEachMemoryRegion(search_inside_region)
804
805 def FindWordList(self, word):
806 aligned_res = []
807 unaligned_res = []
808 def search_inside_region(reader, start, size, location):
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000809 for i in xrange(size - self.PointerSize()):
810 loc = location + i
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000811 if reader._ReadWord(loc) == word:
812 slot = start + (loc - location)
813 if slot % self.PointerSize() == 0:
814 aligned_res.append(slot)
815 else:
816 unaligned_res.append(slot)
817 self.ForEachMemoryRegion(search_inside_region)
818 return (aligned_res, unaligned_res)
819
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100820 def FindLocation(self, address):
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100821 offset = 0
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000822 if self.memory_list64 is not None:
823 for r in self.memory_list64.ranges:
824 if r.start <= address < r.start + r.size:
825 return self.memory_list64.base_rva + offset + address - r.start
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000826 offset += r.size
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000827 if self.memory_list is not None:
828 for r in self.memory_list.ranges:
829 if r.start <= address < r.start + r.memory.data_size:
830 return r.memory.rva + address - r.start
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100831 return None
832
833 def GetDisasmLines(self, address, size):
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000834 def CountUndefinedInstructions(lines):
835 pattern = "<UNDEFINED>"
836 return sum([line.count(pattern) for (ignore, line) in lines])
837
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100838 location = self.FindLocation(address)
839 if location is None: return []
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100840 arch = None
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000841 possible_objdump_flags = [""]
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100842 if self.arch == MD_CPU_ARCHITECTURE_X86:
843 arch = "ia32"
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000844 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
845 arch = "arm"
846 possible_objdump_flags = ["", "--disassembler-options=force-thumb"]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000847 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
848 arch = "arm64"
849 possible_objdump_flags = ["", "--disassembler-options=force-thumb"]
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100850 elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
851 arch = "x64"
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000852 results = [ disasm.GetDisasmLines(self.minidump_name,
853 location,
854 size,
855 arch,
856 False,
857 objdump_flags)
858 for objdump_flags in possible_objdump_flags ]
859 return min(results, key=CountUndefinedInstructions)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100860
861
862 def Dispose(self):
863 self.minidump.close()
864 self.minidump_file.close()
865
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100866 def ExceptionIP(self):
867 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
868 return self.exception_context.rip
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000869 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
870 return self.exception_context.pc
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000871 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
872 return self.exception_context.pc
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100873 elif self.arch == MD_CPU_ARCHITECTURE_X86:
874 return self.exception_context.eip
875
876 def ExceptionSP(self):
877 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
878 return self.exception_context.rsp
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000879 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
880 return self.exception_context.sp
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000881 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
882 return self.exception_context.sp
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100883 elif self.arch == MD_CPU_ARCHITECTURE_X86:
884 return self.exception_context.esp
885
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000886 def ExceptionFP(self):
887 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
888 return self.exception_context.rbp
889 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
890 return None
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000891 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
892 return self.exception_context.fp
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000893 elif self.arch == MD_CPU_ARCHITECTURE_X86:
894 return self.exception_context.ebp
895
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100896 def FormatIntPtr(self, value):
897 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
898 return "%016x" % value
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000899 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
900 return "%08x" % value
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000901 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
902 return "%016x" % value
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100903 elif self.arch == MD_CPU_ARCHITECTURE_X86:
904 return "%08x" % value
905
906 def PointerSize(self):
907 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
908 return 8
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000909 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
910 return 4
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000911 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
912 return 8
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100913 elif self.arch == MD_CPU_ARCHITECTURE_X86:
914 return 4
915
916 def Register(self, name):
917 return self.exception_context.__getattribute__(name)
918
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000919 def ReadMinidumpString(self, rva):
920 string = bytearray(MINIDUMP_STRING.Read(self.minidump, rva).buffer)
921 string = string.decode("utf16")
922 return string[0:len(string) - 1]
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100923
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000924 # Load FUNC records from a BreakPad symbol file
925 #
926 # http://code.google.com/p/google-breakpad/wiki/SymbolFiles
927 #
928 def _LoadSymbolsFrom(self, symfile, baseaddr):
929 print "Loading symbols from %s" % (symfile)
930 funcs = []
931 with open(symfile) as f:
932 for line in f:
933 result = re.match(
934 r"^FUNC ([a-f0-9]+) ([a-f0-9]+) ([a-f0-9]+) (.*)$", line)
935 if result is not None:
936 start = int(result.group(1), 16)
937 size = int(result.group(2), 16)
938 name = result.group(4).rstrip()
939 bisect.insort_left(self.symbols,
940 FuncSymbol(baseaddr + start, size, name))
941 print " ... done"
942
943 def TryLoadSymbolsFor(self, modulename, module):
944 try:
945 symfile = os.path.join(self.symdir,
946 modulename.replace('.', '_') + ".pdb.sym")
947 if os.path.isfile(symfile):
948 self._LoadSymbolsFrom(symfile, module.base_of_image)
949 self.modules_with_symbols.append(module)
950 except Exception as e:
951 print " ... failure (%s)" % (e)
952
953 # Returns true if address is covered by some module that has loaded symbols.
954 def _IsInModuleWithSymbols(self, addr):
955 for module in self.modules_with_symbols:
956 start = module.base_of_image
957 end = start + module.size_of_image
958 if (start <= addr) and (addr < end):
959 return True
960 return False
961
962 # Find symbol covering the given address and return its name in format
963 # <symbol name>+<offset from the start>
964 def FindSymbol(self, addr):
965 if not self._IsInModuleWithSymbols(addr):
966 return None
967
968 i = bisect.bisect_left(self.symbols, addr)
969 symbol = None
970 if (0 < i) and self.symbols[i - 1].Covers(addr):
971 symbol = self.symbols[i - 1]
972 elif (i < len(self.symbols)) and self.symbols[i].Covers(addr):
973 symbol = self.symbols[i]
974 else:
975 return None
976 diff = addr - symbol.start
977 return "%s+0x%x" % (symbol.name, diff)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100978
979
980class Printer(object):
981 """Printer with indentation support."""
982
983 def __init__(self):
984 self.indent = 0
985
986 def Indent(self):
987 self.indent += 2
988
989 def Dedent(self):
990 self.indent -= 2
991
992 def Print(self, string):
993 print "%s%s" % (self._IndentString(), string)
994
995 def PrintLines(self, lines):
996 indent = self._IndentString()
997 print "\n".join("%s%s" % (indent, line) for line in lines)
998
999 def _IndentString(self):
1000 return self.indent * " "
1001
1002
1003ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
1004
1005
1006def FormatDisasmLine(start, heap, line):
1007 line_address = start + line[0]
1008 stack_slot = heap.stack_map.get(line_address)
1009 marker = " "
1010 if stack_slot:
1011 marker = "=>"
1012 code = AnnotateAddresses(heap, line[1])
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001013
1014 # Compute the actual call target which the disassembler is too stupid
1015 # to figure out (it adds the call offset to the disassembly offset rather
1016 # than the absolute instruction address).
1017 if heap.reader.arch == MD_CPU_ARCHITECTURE_X86:
1018 if code.startswith("e8"):
1019 words = code.split()
1020 if len(words) > 6 and words[5] == "call":
1021 offset = int(words[4] + words[3] + words[2] + words[1], 16)
1022 target = (line_address + offset + 5) & 0xFFFFFFFF
1023 code = code.replace(words[6], "0x%08x" % target)
1024 # TODO(jkummerow): port this hack to ARM and x64.
1025
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001026 return "%s%08x %08x: %s" % (marker, line_address, line[0], code)
1027
1028
1029def AnnotateAddresses(heap, line):
1030 extra = []
1031 for m in ADDRESS_RE.finditer(line):
1032 maybe_address = int(m.group(0), 16)
1033 object = heap.FindObject(maybe_address)
1034 if not object: continue
1035 extra.append(str(object))
1036 if len(extra) == 0: return line
1037 return "%s ;; %s" % (line, ", ".join(extra))
1038
1039
1040class HeapObject(object):
1041 def __init__(self, heap, map, address):
1042 self.heap = heap
1043 self.map = map
1044 self.address = address
1045
1046 def Is(self, cls):
1047 return isinstance(self, cls)
1048
1049 def Print(self, p):
1050 p.Print(str(self))
1051
1052 def __str__(self):
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001053 instance_type = "???"
1054 if self.map is not None:
1055 instance_type = INSTANCE_TYPES[self.map.instance_type]
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001056 return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address),
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001057 instance_type)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001058
1059 def ObjectField(self, offset):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001060 field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001061 return self.heap.FindObjectOrSmi(field_value)
1062
1063 def SmiField(self, offset):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001064 field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001065 if (field_value & 1) == 0:
1066 return field_value / 2
1067 return None
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001068
1069
1070class Map(HeapObject):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001071 def Decode(self, offset, size, value):
1072 return (value >> offset) & ((1 << size) - 1)
1073
1074 # Instance Sizes
1075 def InstanceSizesOffset(self):
1076 return self.heap.PointerSize()
1077
1078 def InstanceSizeOffset(self):
1079 return self.InstanceSizesOffset()
1080
1081 def InObjectProperties(self):
1082 return self.InstanceSizeOffset() + 1
1083
1084 def PreAllocatedPropertyFields(self):
1085 return self.InObjectProperties() + 1
1086
1087 def VisitorId(self):
1088 return self.PreAllocatedPropertyFields() + 1
1089
1090 # Instance Attributes
1091 def InstanceAttributesOffset(self):
1092 return self.InstanceSizesOffset() + self.heap.IntSize()
1093
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001094 def InstanceTypeOffset(self):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001095 return self.InstanceAttributesOffset()
1096
1097 def UnusedPropertyFieldsOffset(self):
1098 return self.InstanceTypeOffset() + 1
1099
1100 def BitFieldOffset(self):
1101 return self.UnusedPropertyFieldsOffset() + 1
1102
1103 def BitField2Offset(self):
1104 return self.BitFieldOffset() + 1
1105
1106 # Other fields
1107 def PrototypeOffset(self):
1108 return self.InstanceAttributesOffset() + self.heap.IntSize()
1109
1110 def ConstructorOffset(self):
1111 return self.PrototypeOffset() + self.heap.PointerSize()
1112
1113 def TransitionsOrBackPointerOffset(self):
1114 return self.ConstructorOffset() + self.heap.PointerSize()
1115
1116 def DescriptorsOffset(self):
1117 return self.TransitionsOrBackPointerOffset() + self.heap.PointerSize()
1118
1119 def CodeCacheOffset(self):
1120 return self.DescriptorsOffset() + self.heap.PointerSize()
1121
1122 def DependentCodeOffset(self):
1123 return self.CodeCacheOffset() + self.heap.PointerSize()
1124
1125 def BitField3Offset(self):
1126 return self.DependentCodeOffset() + self.heap.PointerSize()
1127
1128 def ReadByte(self, offset):
1129 return self.heap.reader.ReadU8(self.address + offset)
1130
1131 def Print(self, p):
1132 p.Print("Map(%08x)" % (self.address))
1133 p.Print("- size: %d, inobject: %d, preallocated: %d, visitor: %d" % (
1134 self.ReadByte(self.InstanceSizeOffset()),
1135 self.ReadByte(self.InObjectProperties()),
1136 self.ReadByte(self.PreAllocatedPropertyFields()),
1137 self.VisitorId()))
1138
1139 bitfield = self.ReadByte(self.BitFieldOffset())
1140 bitfield2 = self.ReadByte(self.BitField2Offset())
1141 p.Print("- %s, unused: %d, bf: %d, bf2: %d" % (
1142 INSTANCE_TYPES[self.ReadByte(self.InstanceTypeOffset())],
1143 self.ReadByte(self.UnusedPropertyFieldsOffset()),
1144 bitfield, bitfield2))
1145
1146 p.Print("- kind: %s" % (self.Decode(3, 5, bitfield2)))
1147
1148 bitfield3 = self.ObjectField(self.BitField3Offset())
1149 p.Print(
1150 "- EnumLength: %d NumberOfOwnDescriptors: %d OwnsDescriptors: %s" % (
1151 self.Decode(0, 11, bitfield3),
1152 self.Decode(11, 11, bitfield3),
1153 self.Decode(25, 1, bitfield3)))
1154 p.Print("- IsShared: %s" % (self.Decode(22, 1, bitfield3)))
1155 p.Print("- FunctionWithPrototype: %s" % (self.Decode(23, 1, bitfield3)))
1156 p.Print("- DictionaryMap: %s" % (self.Decode(24, 1, bitfield3)))
1157
1158 descriptors = self.ObjectField(self.DescriptorsOffset())
1159 if descriptors.__class__ == FixedArray:
1160 DescriptorArray(descriptors).Print(p)
1161 else:
1162 p.Print("Descriptors: %s" % (descriptors))
1163
1164 transitions = self.ObjectField(self.TransitionsOrBackPointerOffset())
1165 if transitions.__class__ == FixedArray:
1166 TransitionArray(transitions).Print(p)
1167 else:
1168 p.Print("TransitionsOrBackPointer: %s" % (transitions))
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001169
1170 def __init__(self, heap, map, address):
1171 HeapObject.__init__(self, heap, map, address)
1172 self.instance_type = \
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001173 heap.reader.ReadU8(self.address + self.InstanceTypeOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001174
1175
1176class String(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001177 def LengthOffset(self):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001178 # First word after the map is the hash, the second is the length.
1179 return self.heap.PointerSize() * 2
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001180
1181 def __init__(self, heap, map, address):
1182 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001183 self.length = self.SmiField(self.LengthOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001184
1185 def GetChars(self):
1186 return "?string?"
1187
1188 def Print(self, p):
1189 p.Print(str(self))
1190
1191 def __str__(self):
1192 return "\"%s\"" % self.GetChars()
1193
1194
1195class SeqString(String):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001196 def CharsOffset(self):
1197 return self.heap.PointerSize() * 3
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001198
1199 def __init__(self, heap, map, address):
1200 String.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001201 self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(),
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001202 self.length)
1203
1204 def GetChars(self):
1205 return self.chars
1206
1207
1208class ExternalString(String):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001209 # TODO(vegorov) fix ExternalString for X64 architecture
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001210 RESOURCE_OFFSET = 12
1211
1212 WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
1213 WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
1214
1215 def __init__(self, heap, map, address):
1216 String.__init__(self, heap, map, address)
1217 reader = heap.reader
1218 self.resource = \
1219 reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
1220 self.chars = "?external string?"
1221 if not reader.IsValidAddress(self.resource): return
1222 string_impl_address = self.resource + \
1223 ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
1224 if not reader.IsValidAddress(string_impl_address): return
1225 string_impl = reader.ReadU32(string_impl_address)
1226 chars_ptr_address = string_impl + \
1227 ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
1228 if not reader.IsValidAddress(chars_ptr_address): return
1229 chars_ptr = reader.ReadU32(chars_ptr_address)
1230 if not reader.IsValidAddress(chars_ptr): return
1231 raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
1232 self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
1233
1234 def GetChars(self):
1235 return self.chars
1236
1237
1238class ConsString(String):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001239 def LeftOffset(self):
1240 return self.heap.PointerSize() * 3
1241
1242 def RightOffset(self):
1243 return self.heap.PointerSize() * 4
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001244
1245 def __init__(self, heap, map, address):
1246 String.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001247 self.left = self.ObjectField(self.LeftOffset())
1248 self.right = self.ObjectField(self.RightOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001249
1250 def GetChars(self):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001251 try:
1252 return self.left.GetChars() + self.right.GetChars()
1253 except:
1254 return "***CAUGHT EXCEPTION IN GROKDUMP***"
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001255
1256
1257class Oddball(HeapObject):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001258 # Should match declarations in objects.h
1259 KINDS = [
1260 "False",
1261 "True",
1262 "TheHole",
1263 "Null",
1264 "ArgumentMarker",
1265 "Undefined",
1266 "Other"
1267 ]
1268
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001269 def ToStringOffset(self):
1270 return self.heap.PointerSize()
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001271
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001272 def ToNumberOffset(self):
1273 return self.ToStringOffset() + self.heap.PointerSize()
1274
1275 def KindOffset(self):
1276 return self.ToNumberOffset() + self.heap.PointerSize()
1277
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001278 def __init__(self, heap, map, address):
1279 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001280 self.to_string = self.ObjectField(self.ToStringOffset())
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001281 self.kind = self.SmiField(self.KindOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001282
1283 def Print(self, p):
1284 p.Print(str(self))
1285
1286 def __str__(self):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001287 if self.to_string:
1288 return "Oddball(%08x, <%s>)" % (self.address, str(self.to_string))
1289 else:
1290 kind = "???"
1291 if 0 <= self.kind < len(Oddball.KINDS):
1292 kind = Oddball.KINDS[self.kind]
1293 return "Oddball(%08x, kind=%s)" % (self.address, kind)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001294
1295
1296class FixedArray(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001297 def LengthOffset(self):
1298 return self.heap.PointerSize()
1299
1300 def ElementsOffset(self):
1301 return self.heap.PointerSize() * 2
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001302
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001303 def MemberOffset(self, i):
1304 return self.ElementsOffset() + self.heap.PointerSize() * i
1305
1306 def Get(self, i):
1307 return self.ObjectField(self.MemberOffset(i))
1308
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001309 def __init__(self, heap, map, address):
1310 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001311 self.length = self.SmiField(self.LengthOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001312
1313 def Print(self, p):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001314 p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address))
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001315 p.Indent()
1316 p.Print("length: %d" % self.length)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001317 base_offset = self.ElementsOffset()
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001318 for i in xrange(self.length):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001319 offset = base_offset + 4 * i
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001320 try:
1321 p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
1322 except TypeError:
1323 p.Dedent()
1324 p.Print("...")
1325 p.Print("}")
1326 return
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001327 p.Dedent()
1328 p.Print("}")
1329
1330 def __str__(self):
1331 return "FixedArray(%08x, length=%d)" % (self.address, self.length)
1332
1333
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001334class DescriptorArray(object):
1335 def __init__(self, array):
1336 self.array = array
1337
1338 def Length(self):
1339 return self.array.Get(0)
1340
1341 def Decode(self, offset, size, value):
1342 return (value >> offset) & ((1 << size) - 1)
1343
1344 TYPES = [
1345 "normal",
1346 "field",
1347 "function",
1348 "callbacks"
1349 ]
1350
1351 def Type(self, value):
1352 return DescriptorArray.TYPES[self.Decode(0, 3, value)]
1353
1354 def Attributes(self, value):
1355 attributes = self.Decode(3, 3, value)
1356 result = []
1357 if (attributes & 0): result += ["ReadOnly"]
1358 if (attributes & 1): result += ["DontEnum"]
1359 if (attributes & 2): result += ["DontDelete"]
1360 return "[" + (",".join(result)) + "]"
1361
1362 def Deleted(self, value):
1363 return self.Decode(6, 1, value) == 1
1364
1365 def FieldIndex(self, value):
1366 return self.Decode(20, 11, value)
1367
1368 def Pointer(self, value):
1369 return self.Decode(6, 11, value)
1370
1371 def Details(self, di, value):
1372 return (
1373 di,
1374 self.Type(value),
1375 self.Attributes(value),
1376 self.FieldIndex(value),
1377 self.Pointer(value)
1378 )
1379
1380
1381 def Print(self, p):
1382 length = self.Length()
1383 array = self.array
1384
1385 p.Print("Descriptors(%08x, length=%d)" % (array.address, length))
1386 p.Print("[et] %s" % (array.Get(1)))
1387
1388 for di in xrange(length):
1389 i = 2 + di * 3
1390 p.Print("0x%x" % (array.address + array.MemberOffset(i)))
1391 p.Print("[%i] name: %s" % (di, array.Get(i + 0)))
1392 p.Print("[%i] details: %s %s field-index %i pointer %i" % \
1393 self.Details(di, array.Get(i + 1)))
1394 p.Print("[%i] value: %s" % (di, array.Get(i + 2)))
1395
1396 end = self.array.length // 3
1397 if length != end:
1398 p.Print("[%i-%i] slack descriptors" % (length, end))
1399
1400
1401class TransitionArray(object):
1402 def __init__(self, array):
1403 self.array = array
1404
1405 def IsSimpleTransition(self):
1406 return self.array.length <= 2
1407
1408 def Length(self):
1409 # SimpleTransition cases
1410 if self.IsSimpleTransition():
1411 return self.array.length - 1
1412 return (self.array.length - 3) // 2
1413
1414 def Print(self, p):
1415 length = self.Length()
1416 array = self.array
1417
1418 p.Print("Transitions(%08x, length=%d)" % (array.address, length))
1419 p.Print("[backpointer] %s" % (array.Get(0)))
1420 if self.IsSimpleTransition():
1421 if length == 1:
1422 p.Print("[simple target] %s" % (array.Get(1)))
1423 return
1424
1425 elements = array.Get(1)
1426 if elements is not None:
1427 p.Print("[elements ] %s" % (elements))
1428
1429 prototype = array.Get(2)
1430 if prototype is not None:
1431 p.Print("[prototype ] %s" % (prototype))
1432
1433 for di in xrange(length):
1434 i = 3 + di * 2
1435 p.Print("[%i] symbol: %s" % (di, array.Get(i + 0)))
1436 p.Print("[%i] target: %s" % (di, array.Get(i + 1)))
1437
1438
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001439class JSFunction(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001440 def CodeEntryOffset(self):
1441 return 3 * self.heap.PointerSize()
1442
1443 def SharedOffset(self):
1444 return 5 * self.heap.PointerSize()
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001445
1446 def __init__(self, heap, map, address):
1447 HeapObject.__init__(self, heap, map, address)
1448 code_entry = \
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001449 heap.reader.ReadU32(self.address + self.CodeEntryOffset())
1450 self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1)
1451 self.shared = self.ObjectField(self.SharedOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001452
1453 def Print(self, p):
1454 source = "\n".join(" %s" % line for line in self._GetSource().split("\n"))
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001455 p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address))
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001456 p.Indent()
1457 p.Print("inferred name: %s" % self.shared.inferred_name)
1458 if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
1459 p.Print("script name: %s" % self.shared.script.name)
1460 p.Print("source:")
1461 p.PrintLines(self._GetSource().split("\n"))
1462 p.Print("code:")
1463 self.code.Print(p)
1464 if self.code != self.shared.code:
1465 p.Print("unoptimized code:")
1466 self.shared.code.Print(p)
1467 p.Dedent()
1468 p.Print("}")
1469
1470 def __str__(self):
1471 inferred_name = ""
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001472 if self.shared is not None and self.shared.Is(SharedFunctionInfo):
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001473 inferred_name = self.shared.inferred_name
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001474 return "JSFunction(%s, %s) " % \
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001475 (self.heap.reader.FormatIntPtr(self.address), inferred_name)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001476
1477 def _GetSource(self):
1478 source = "?source?"
1479 start = self.shared.start_position
1480 end = self.shared.end_position
1481 if not self.shared.script.Is(Script): return source
1482 script_source = self.shared.script.source
1483 if not script_source.Is(String): return source
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001484 if start and end:
1485 source = script_source.GetChars()[start:end]
1486 return source
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001487
1488
1489class SharedFunctionInfo(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001490 def CodeOffset(self):
1491 return 2 * self.heap.PointerSize()
1492
1493 def ScriptOffset(self):
1494 return 7 * self.heap.PointerSize()
1495
1496 def InferredNameOffset(self):
1497 return 9 * self.heap.PointerSize()
1498
1499 def EndPositionOffset(self):
1500 return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
1501
1502 def StartPositionAndTypeOffset(self):
1503 return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001504
1505 def __init__(self, heap, map, address):
1506 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001507 self.code = self.ObjectField(self.CodeOffset())
1508 self.script = self.ObjectField(self.ScriptOffset())
1509 self.inferred_name = self.ObjectField(self.InferredNameOffset())
1510 if heap.PointerSize() == 8:
1511 start_position_and_type = \
1512 heap.reader.ReadU32(self.StartPositionAndTypeOffset())
1513 self.start_position = start_position_and_type >> 2
1514 pseudo_smi_end_position = \
1515 heap.reader.ReadU32(self.EndPositionOffset())
1516 self.end_position = pseudo_smi_end_position >> 2
1517 else:
1518 start_position_and_type = \
1519 self.SmiField(self.StartPositionAndTypeOffset())
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001520 if start_position_and_type:
1521 self.start_position = start_position_and_type >> 2
1522 else:
1523 self.start_position = None
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001524 self.end_position = \
1525 self.SmiField(self.EndPositionOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001526
1527
1528class Script(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001529 def SourceOffset(self):
1530 return self.heap.PointerSize()
1531
1532 def NameOffset(self):
1533 return self.SourceOffset() + self.heap.PointerSize()
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001534
1535 def __init__(self, heap, map, address):
1536 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001537 self.source = self.ObjectField(self.SourceOffset())
1538 self.name = self.ObjectField(self.NameOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001539
1540
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001541class CodeCache(HeapObject):
1542 def DefaultCacheOffset(self):
1543 return self.heap.PointerSize()
1544
1545 def NormalTypeCacheOffset(self):
1546 return self.DefaultCacheOffset() + self.heap.PointerSize()
1547
1548 def __init__(self, heap, map, address):
1549 HeapObject.__init__(self, heap, map, address)
1550 self.default_cache = self.ObjectField(self.DefaultCacheOffset())
1551 self.normal_type_cache = self.ObjectField(self.NormalTypeCacheOffset())
1552
1553 def Print(self, p):
1554 p.Print("CodeCache(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1555 p.Indent()
1556 p.Print("default cache: %s" % self.default_cache)
1557 p.Print("normal type cache: %s" % self.normal_type_cache)
1558 p.Dedent()
1559 p.Print("}")
1560
1561
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001562class Code(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001563 CODE_ALIGNMENT_MASK = (1 << 5) - 1
1564
1565 def InstructionSizeOffset(self):
1566 return self.heap.PointerSize()
1567
1568 @staticmethod
1569 def HeaderSize(heap):
1570 return (heap.PointerSize() + heap.IntSize() + \
1571 4 * heap.PointerSize() + 3 * heap.IntSize() + \
1572 Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001573
1574 def __init__(self, heap, map, address):
1575 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001576 self.entry = self.address + Code.HeaderSize(heap)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001577 self.instruction_size = \
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001578 heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001579
1580 def Print(self, p):
1581 lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001582 p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address))
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001583 p.Indent()
1584 p.Print("instruction_size: %d" % self.instruction_size)
1585 p.PrintLines(self._FormatLine(line) for line in lines)
1586 p.Dedent()
1587 p.Print("}")
1588
1589 def _FormatLine(self, line):
1590 return FormatDisasmLine(self.entry, self.heap, line)
1591
1592
1593class V8Heap(object):
1594 CLASS_MAP = {
1595 "SYMBOL_TYPE": SeqString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001596 "ONE_BYTE_SYMBOL_TYPE": SeqString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001597 "CONS_SYMBOL_TYPE": ConsString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001598 "CONS_ONE_BYTE_SYMBOL_TYPE": ConsString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001599 "EXTERNAL_SYMBOL_TYPE": ExternalString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001600 "EXTERNAL_SYMBOL_WITH_ONE_BYTE_DATA_TYPE": ExternalString,
1601 "EXTERNAL_ONE_BYTE_SYMBOL_TYPE": ExternalString,
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001602 "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001603 "SHORT_EXTERNAL_SYMBOL_WITH_ONE_BYTE_DATA_TYPE": ExternalString,
1604 "SHORT_EXTERNAL_ONE_BYTE_SYMBOL_TYPE": ExternalString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001605 "STRING_TYPE": SeqString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001606 "ONE_BYTE_STRING_TYPE": SeqString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001607 "CONS_STRING_TYPE": ConsString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001608 "CONS_ONE_BYTE_STRING_TYPE": ConsString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001609 "EXTERNAL_STRING_TYPE": ExternalString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001610 "EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE": ExternalString,
1611 "EXTERNAL_ONE_BYTE_STRING_TYPE": ExternalString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001612 "MAP_TYPE": Map,
1613 "ODDBALL_TYPE": Oddball,
1614 "FIXED_ARRAY_TYPE": FixedArray,
1615 "JS_FUNCTION_TYPE": JSFunction,
1616 "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
1617 "SCRIPT_TYPE": Script,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001618 "CODE_CACHE_TYPE": CodeCache,
1619 "CODE_TYPE": Code,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001620 }
1621
1622 def __init__(self, reader, stack_map):
1623 self.reader = reader
1624 self.stack_map = stack_map
1625 self.objects = {}
1626
1627 def FindObjectOrSmi(self, tagged_address):
1628 if (tagged_address & 1) == 0: return tagged_address / 2
1629 return self.FindObject(tagged_address)
1630
1631 def FindObject(self, tagged_address):
1632 if tagged_address in self.objects:
1633 return self.objects[tagged_address]
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001634 if (tagged_address & self.ObjectAlignmentMask()) != 1: return None
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001635 address = tagged_address - 1
1636 if not self.reader.IsValidAddress(address): return None
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001637 map_tagged_address = self.reader.ReadUIntPtr(address)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001638 if tagged_address == map_tagged_address:
1639 # Meta map?
1640 meta_map = Map(self, None, address)
1641 instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
1642 if instance_type_name != "MAP_TYPE": return None
1643 meta_map.map = meta_map
1644 object = meta_map
1645 else:
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001646 map = self.FindMap(map_tagged_address)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001647 if map is None: return None
1648 instance_type_name = INSTANCE_TYPES.get(map.instance_type)
1649 if instance_type_name is None: return None
1650 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
1651 object = cls(self, map, address)
1652 self.objects[tagged_address] = object
1653 return object
1654
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001655 def FindMap(self, tagged_address):
1656 if (tagged_address & self.MapAlignmentMask()) != 1: return None
1657 address = tagged_address - 1
1658 if not self.reader.IsValidAddress(address): return None
1659 object = Map(self, None, address)
1660 return object
1661
1662 def IntSize(self):
1663 return 4
1664
1665 def PointerSize(self):
1666 return self.reader.PointerSize()
1667
1668 def ObjectAlignmentMask(self):
1669 return self.PointerSize() - 1
1670
1671 def MapAlignmentMask(self):
1672 if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64:
1673 return (1 << 4) - 1
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001674 elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM:
1675 return (1 << 4) - 1
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001676 elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM64:
1677 return (1 << 4) - 1
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001678 elif self.reader.arch == MD_CPU_ARCHITECTURE_X86:
1679 return (1 << 5) - 1
1680
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001681 def PageAlignmentMask(self):
1682 return (1 << 20) - 1
1683
1684
1685class KnownObject(HeapObject):
1686 def __init__(self, heap, known_name):
1687 HeapObject.__init__(self, heap, None, None)
1688 self.known_name = known_name
1689
1690 def __str__(self):
1691 return "<%s>" % self.known_name
1692
1693
1694class KnownMap(HeapObject):
1695 def __init__(self, heap, known_name, instance_type):
1696 HeapObject.__init__(self, heap, None, None)
1697 self.instance_type = instance_type
1698 self.known_name = known_name
1699
1700 def __str__(self):
1701 return "<%s>" % self.known_name
1702
1703
1704COMMENT_RE = re.compile(r"^C (0x[0-9a-fA-F]+) (.*)$")
1705PAGEADDRESS_RE = re.compile(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001706 r"^P (mappage|oldpage) (0x[0-9a-fA-F]+)$")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001707
1708
1709class InspectionInfo(object):
1710 def __init__(self, minidump_name, reader):
1711 self.comment_file = minidump_name + ".comments"
1712 self.address_comments = {}
1713 self.page_address = {}
1714 if os.path.exists(self.comment_file):
1715 with open(self.comment_file, "r") as f:
1716 lines = f.readlines()
1717 f.close()
1718
1719 for l in lines:
1720 m = COMMENT_RE.match(l)
1721 if m:
1722 self.address_comments[int(m.group(1), 0)] = m.group(2)
1723 m = PAGEADDRESS_RE.match(l)
1724 if m:
1725 self.page_address[m.group(1)] = int(m.group(2), 0)
1726 self.reader = reader
1727 self.styles = {}
1728 self.color_addresses()
1729 return
1730
1731 def get_page_address(self, page_kind):
1732 return self.page_address.get(page_kind, 0)
1733
1734 def save_page_address(self, page_kind, address):
1735 with open(self.comment_file, "a") as f:
1736 f.write("P %s 0x%x\n" % (page_kind, address))
1737 f.close()
1738
1739 def color_addresses(self):
1740 # Color all stack addresses.
1741 exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
1742 stack_top = self.reader.ExceptionSP()
1743 stack_bottom = exception_thread.stack.start + \
1744 exception_thread.stack.memory.data_size
1745 frame_pointer = self.reader.ExceptionFP()
1746 self.styles[frame_pointer] = "frame"
1747 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
1748 self.styles[slot] = "stackaddress"
1749 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
1750 maybe_address = self.reader.ReadUIntPtr(slot)
1751 self.styles[maybe_address] = "stackval"
1752 if slot == frame_pointer:
1753 self.styles[slot] = "frame"
1754 frame_pointer = maybe_address
1755 self.styles[self.reader.ExceptionIP()] = "pc"
1756
1757 def get_style_class(self, address):
1758 return self.styles.get(address, None)
1759
1760 def get_style_class_string(self, address):
1761 style = self.get_style_class(address)
1762 if style != None:
1763 return " class=\"%s\" " % style
1764 else:
1765 return ""
1766
1767 def set_comment(self, address, comment):
1768 self.address_comments[address] = comment
1769 with open(self.comment_file, "a") as f:
1770 f.write("C 0x%x %s\n" % (address, comment))
1771 f.close()
1772
1773 def get_comment(self, address):
1774 return self.address_comments.get(address, "")
1775
1776
1777class InspectionPadawan(object):
1778 """The padawan can improve annotations by sensing well-known objects."""
1779 def __init__(self, reader, heap):
1780 self.reader = reader
1781 self.heap = heap
1782 self.known_first_map_page = 0
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001783 self.known_first_old_page = 0
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001784
1785 def __getattr__(self, name):
1786 """An InspectionPadawan can be used instead of V8Heap, even though
1787 it does not inherit from V8Heap (aka. mixin)."""
1788 return getattr(self.heap, name)
1789
1790 def GetPageOffset(self, tagged_address):
1791 return tagged_address & self.heap.PageAlignmentMask()
1792
1793 def IsInKnownMapSpace(self, tagged_address):
1794 page_address = tagged_address & ~self.heap.PageAlignmentMask()
1795 return page_address == self.known_first_map_page
1796
1797 def IsInKnownOldSpace(self, tagged_address):
1798 page_address = tagged_address & ~self.heap.PageAlignmentMask()
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001799 return page_address == self.known_first_old_page
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001800
1801 def ContainingKnownOldSpaceName(self, tagged_address):
1802 page_address = tagged_address & ~self.heap.PageAlignmentMask()
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001803 if page_address == self.known_first_old_page: return "OLD_SPACE"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001804 return None
1805
1806 def SenseObject(self, tagged_address):
1807 if self.IsInKnownOldSpace(tagged_address):
1808 offset = self.GetPageOffset(tagged_address)
1809 lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset)
1810 known_obj_name = KNOWN_OBJECTS.get(lookup_key)
1811 if known_obj_name:
1812 return KnownObject(self, known_obj_name)
1813 if self.IsInKnownMapSpace(tagged_address):
1814 known_map = self.SenseMap(tagged_address)
1815 if known_map:
1816 return known_map
1817 found_obj = self.heap.FindObject(tagged_address)
1818 if found_obj: return found_obj
1819 address = tagged_address - 1
1820 if self.reader.IsValidAddress(address):
1821 map_tagged_address = self.reader.ReadUIntPtr(address)
1822 map = self.SenseMap(map_tagged_address)
1823 if map is None: return None
1824 instance_type_name = INSTANCE_TYPES.get(map.instance_type)
1825 if instance_type_name is None: return None
1826 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
1827 return cls(self, map, address)
1828 return None
1829
1830 def SenseMap(self, tagged_address):
1831 if self.IsInKnownMapSpace(tagged_address):
1832 offset = self.GetPageOffset(tagged_address)
1833 known_map_info = KNOWN_MAPS.get(offset)
1834 if known_map_info:
1835 known_map_type, known_map_name = known_map_info
1836 return KnownMap(self, known_map_name, known_map_type)
1837 found_map = self.heap.FindMap(tagged_address)
1838 if found_map: return found_map
1839 return None
1840
1841 def FindObjectOrSmi(self, tagged_address):
1842 """When used as a mixin in place of V8Heap."""
1843 found_obj = self.SenseObject(tagged_address)
1844 if found_obj: return found_obj
1845 if (tagged_address & 1) == 0:
1846 return "Smi(%d)" % (tagged_address / 2)
1847 else:
1848 return "Unknown(%s)" % self.reader.FormatIntPtr(tagged_address)
1849
1850 def FindObject(self, tagged_address):
1851 """When used as a mixin in place of V8Heap."""
1852 raise NotImplementedError
1853
1854 def FindMap(self, tagged_address):
1855 """When used as a mixin in place of V8Heap."""
1856 raise NotImplementedError
1857
1858 def PrintKnowledge(self):
1859 print " known_first_map_page = %s\n"\
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001860 " known_first_old_page = %s" % (
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001861 self.reader.FormatIntPtr(self.known_first_map_page),
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001862 self.reader.FormatIntPtr(self.known_first_old_page))
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001863
1864WEB_HEADER = """
1865<!DOCTYPE html>
1866<html>
1867<head>
1868<meta content="text/html; charset=utf-8" http-equiv="content-type">
1869<style media="screen" type="text/css">
1870
1871.code {
1872 font-family: monospace;
1873}
1874
1875.dmptable {
1876 border-collapse : collapse;
1877 border-spacing : 0px;
1878}
1879
1880.codedump {
1881 border-collapse : collapse;
1882 border-spacing : 0px;
1883}
1884
1885.addrcomments {
1886 border : 0px;
1887}
1888
1889.register {
1890 padding-right : 1em;
1891}
1892
1893.header {
1894 clear : both;
1895}
1896
1897.header .navigation {
1898 float : left;
1899}
1900
1901.header .dumpname {
1902 float : right;
1903}
1904
1905tr.highlight-line {
1906 background-color : yellow;
1907}
1908
1909.highlight {
1910 background-color : magenta;
1911}
1912
1913tr.inexact-highlight-line {
1914 background-color : pink;
1915}
1916
1917input {
1918 background-color: inherit;
1919 border: 1px solid LightGray;
1920}
1921
1922.dumpcomments {
1923 border : 1px solid LightGray;
1924 width : 32em;
1925}
1926
1927.regions td {
1928 padding:0 15px 0 15px;
1929}
1930
1931.stackframe td {
1932 background-color : cyan;
1933}
1934
1935.stackaddress {
1936 background-color : LightGray;
1937}
1938
1939.stackval {
1940 background-color : LightCyan;
1941}
1942
1943.frame {
1944 background-color : cyan;
1945}
1946
1947.commentinput {
1948 width : 20em;
1949}
1950
1951a.nodump:visited {
1952 color : black;
1953 text-decoration : none;
1954}
1955
1956a.nodump:link {
1957 color : black;
1958 text-decoration : none;
1959}
1960
1961a:visited {
1962 color : blueviolet;
1963}
1964
1965a:link {
1966 color : blue;
1967}
1968
1969.disasmcomment {
1970 color : DarkGreen;
1971}
1972
1973</style>
1974
1975<script type="application/javascript">
1976
1977var address_str = "address-";
1978var address_len = address_str.length;
1979
1980function comment() {
1981 var s = event.srcElement.id;
1982 var index = s.indexOf(address_str);
1983 if (index >= 0) {
1984 send_comment(s.substring(index + address_len), event.srcElement.value);
1985 }
1986}
1987
1988function send_comment(address, comment) {
1989 xmlhttp = new XMLHttpRequest();
1990 address = encodeURIComponent(address)
1991 comment = encodeURIComponent(comment)
1992 xmlhttp.open("GET",
1993 "setcomment?%(query_dump)s&address=" + address +
1994 "&comment=" + comment, true);
1995 xmlhttp.send();
1996}
1997
1998var dump_str = "dump-";
1999var dump_len = dump_str.length;
2000
2001function dump_comment() {
2002 var s = event.srcElement.id;
2003 var index = s.indexOf(dump_str);
2004 if (index >= 0) {
2005 send_dump_desc(s.substring(index + dump_len), event.srcElement.value);
2006 }
2007}
2008
2009function send_dump_desc(name, desc) {
2010 xmlhttp = new XMLHttpRequest();
2011 name = encodeURIComponent(name)
2012 desc = encodeURIComponent(desc)
2013 xmlhttp.open("GET",
2014 "setdumpdesc?dump=" + name +
2015 "&description=" + desc, true);
2016 xmlhttp.send();
2017}
2018
2019function onpage(kind, address) {
2020 xmlhttp = new XMLHttpRequest();
2021 kind = encodeURIComponent(kind)
2022 address = encodeURIComponent(address)
2023 xmlhttp.onreadystatechange = function() {
2024 if (xmlhttp.readyState==4 && xmlhttp.status==200) {
2025 location.reload(true)
2026 }
2027 };
2028 xmlhttp.open("GET",
2029 "setpageaddress?%(query_dump)s&kind=" + kind +
2030 "&address=" + address);
2031 xmlhttp.send();
2032}
2033
2034</script>
2035
2036<title>Dump %(dump_name)s</title>
2037</head>
2038
2039<body>
2040 <div class="header">
2041 <form class="navigation" action="search.html">
2042 <a href="summary.html?%(query_dump)s">Context info</a>&nbsp;&nbsp;&nbsp;
2043 <a href="info.html?%(query_dump)s">Dump info</a>&nbsp;&nbsp;&nbsp;
2044 <a href="modules.html?%(query_dump)s">Modules</a>&nbsp;&nbsp;&nbsp;
2045 &nbsp;
2046 <input type="search" name="val">
2047 <input type="submit" name="search" value="Search">
2048 <input type="hidden" name="dump" value="%(dump_name)s">
2049 </form>
2050 <form class="navigation" action="disasm.html#highlight">
2051 &nbsp;
2052 &nbsp;
2053 &nbsp;
2054 <input type="search" name="val">
2055 <input type="submit" name="disasm" value="Disasm">
2056 &nbsp;
2057 &nbsp;
2058 &nbsp;
2059 <a href="dumps.html">Dumps...</a>
2060 </form>
2061 </div>
2062 <br>
2063 <hr>
2064"""
2065
2066
2067WEB_FOOTER = """
2068</body>
2069</html>
2070"""
2071
2072
2073class WebParameterError(Exception):
2074 def __init__(self, message):
2075 Exception.__init__(self, message)
2076
2077
2078class InspectionWebHandler(BaseHTTPServer.BaseHTTPRequestHandler):
2079 def formatter(self, query_components):
2080 name = query_components.get("dump", [None])[0]
2081 return self.server.get_dump_formatter(name)
2082
2083 def send_success_html_headers(self):
2084 self.send_response(200)
2085 self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
2086 self.send_header("Pragma", "no-cache")
2087 self.send_header("Expires", "0")
2088 self.send_header('Content-type','text/html')
2089 self.end_headers()
2090 return
2091
2092 def do_GET(self):
2093 try:
2094 parsedurl = urlparse.urlparse(self.path)
2095 query_components = urlparse.parse_qs(parsedurl.query)
2096 if parsedurl.path == "/dumps.html":
2097 self.send_success_html_headers()
2098 self.server.output_dumps(self.wfile)
2099 elif parsedurl.path == "/summary.html":
2100 self.send_success_html_headers()
2101 self.formatter(query_components).output_summary(self.wfile)
2102 elif parsedurl.path == "/info.html":
2103 self.send_success_html_headers()
2104 self.formatter(query_components).output_info(self.wfile)
2105 elif parsedurl.path == "/modules.html":
2106 self.send_success_html_headers()
2107 self.formatter(query_components).output_modules(self.wfile)
2108 elif parsedurl.path == "/search.html":
2109 address = query_components.get("val", [])
2110 if len(address) != 1:
2111 self.send_error(404, "Invalid params")
2112 return
2113 self.send_success_html_headers()
2114 self.formatter(query_components).output_search_res(
2115 self.wfile, address[0])
2116 elif parsedurl.path == "/disasm.html":
2117 address = query_components.get("val", [])
2118 exact = query_components.get("exact", ["on"])
2119 if len(address) != 1:
2120 self.send_error(404, "Invalid params")
2121 return
2122 self.send_success_html_headers()
2123 self.formatter(query_components).output_disasm(
2124 self.wfile, address[0], exact[0])
2125 elif parsedurl.path == "/data.html":
2126 address = query_components.get("val", [])
2127 datakind = query_components.get("type", ["address"])
2128 if len(address) == 1 and len(datakind) == 1:
2129 self.send_success_html_headers()
2130 self.formatter(query_components).output_data(
2131 self.wfile, address[0], datakind[0])
2132 else:
2133 self.send_error(404,'Invalid params')
2134 elif parsedurl.path == "/setdumpdesc":
2135 name = query_components.get("dump", [""])
2136 description = query_components.get("description", [""])
2137 if len(name) == 1 and len(description) == 1:
2138 name = name[0]
2139 description = description[0]
2140 if self.server.set_dump_desc(name, description):
2141 self.send_success_html_headers()
2142 self.wfile.write("OK")
2143 return
2144 self.send_error(404,'Invalid params')
2145 elif parsedurl.path == "/setcomment":
2146 address = query_components.get("address", [])
2147 comment = query_components.get("comment", [""])
2148 if len(address) == 1 and len(comment) == 1:
2149 address = address[0]
2150 comment = comment[0]
2151 self.formatter(query_components).set_comment(address, comment)
2152 self.send_success_html_headers()
2153 self.wfile.write("OK")
2154 else:
2155 self.send_error(404,'Invalid params')
2156 elif parsedurl.path == "/setpageaddress":
2157 kind = query_components.get("kind", [])
2158 address = query_components.get("address", [""])
2159 if len(kind) == 1 and len(address) == 1:
2160 kind = kind[0]
2161 address = address[0]
2162 self.formatter(query_components).set_page_address(kind, address)
2163 self.send_success_html_headers()
2164 self.wfile.write("OK")
2165 else:
2166 self.send_error(404,'Invalid params')
2167 else:
2168 self.send_error(404,'File Not Found: %s' % self.path)
2169
2170 except IOError:
2171 self.send_error(404,'File Not Found: %s' % self.path)
2172
2173 except WebParameterError as e:
2174 self.send_error(404, 'Web parameter error: %s' % e.message)
2175
2176
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00002177HTML_REG_FORMAT = "<span class=\"register\"><b>%s</b>:&nbsp;%s</span><br/>\n"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002178
2179
2180class InspectionWebFormatter(object):
2181 CONTEXT_FULL = 0
2182 CONTEXT_SHORT = 1
2183
2184 def __init__(self, switches, minidump_name, http_server):
2185 self.dumpfilename = os.path.split(minidump_name)[1]
2186 self.encfilename = urllib.urlencode({ 'dump' : self.dumpfilename })
2187 self.reader = MinidumpReader(switches, minidump_name)
2188 self.server = http_server
2189
2190 # Set up the heap
2191 exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
2192 stack_top = self.reader.ExceptionSP()
2193 stack_bottom = exception_thread.stack.start + \
2194 exception_thread.stack.memory.data_size
2195 stack_map = {self.reader.ExceptionIP(): -1}
2196 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
2197 maybe_address = self.reader.ReadUIntPtr(slot)
2198 if not maybe_address in stack_map:
2199 stack_map[maybe_address] = slot
2200 self.heap = V8Heap(self.reader, stack_map)
2201
2202 self.padawan = InspectionPadawan(self.reader, self.heap)
2203 self.comments = InspectionInfo(minidump_name, self.reader)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00002204 self.padawan.known_first_old_page = (
2205 self.comments.get_page_address("oldpage"))
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002206 self.padawan.known_first_map_page = (
2207 self.comments.get_page_address("mappage"))
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002208
2209 def set_comment(self, straddress, comment):
2210 try:
2211 address = int(straddress, 0)
2212 self.comments.set_comment(address, comment)
2213 except ValueError:
2214 print "Invalid address"
2215
2216 def set_page_address(self, kind, straddress):
2217 try:
2218 address = int(straddress, 0)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00002219 if kind == "oldpage":
2220 self.padawan.known_first_old_page = address
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002221 elif kind == "mappage":
2222 self.padawan.known_first_map_page = address
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002223 self.comments.save_page_address(kind, address)
2224 except ValueError:
2225 print "Invalid address"
2226
2227 def td_from_address(self, f, address):
2228 f.write("<td %s>" % self.comments.get_style_class_string(address))
2229
2230 def format_address(self, maybeaddress, straddress = None):
2231 if maybeaddress is None:
2232 return "not in dump"
2233 else:
2234 if straddress is None:
2235 straddress = "0x" + self.reader.FormatIntPtr(maybeaddress)
2236 style_class = ""
2237 if not self.reader.IsValidAddress(maybeaddress):
2238 style_class = " class=\"nodump\""
2239 return ("<a %s href=\"search.html?%s&amp;val=%s\">%s</a>" %
2240 (style_class, self.encfilename, straddress, straddress))
2241
2242 def output_header(self, f):
2243 f.write(WEB_HEADER %
2244 { "query_dump" : self.encfilename,
2245 "dump_name" : cgi.escape(self.dumpfilename) })
2246
2247 def output_footer(self, f):
2248 f.write(WEB_FOOTER)
2249
2250 MAX_CONTEXT_STACK = 4096
2251
2252 def output_summary(self, f):
2253 self.output_header(f)
2254 f.write('<div class="code">')
2255 self.output_context(f, InspectionWebFormatter.CONTEXT_SHORT)
2256 self.output_disasm_pc(f)
2257
2258 # Output stack
2259 exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
2260 stack_bottom = exception_thread.stack.start + \
2261 min(exception_thread.stack.memory.data_size, self.MAX_CONTEXT_STACK)
2262 stack_top = self.reader.ExceptionSP()
2263 self.output_words(f, stack_top - 16, stack_bottom, stack_top, "Stack")
2264
2265 f.write('</div>')
2266 self.output_footer(f)
2267 return
2268
2269 def output_info(self, f):
2270 self.output_header(f)
2271 f.write("<h3>Dump info</h3>\n")
2272 f.write("Description: ")
2273 self.server.output_dump_desc_field(f, self.dumpfilename)
2274 f.write("<br>\n")
2275 f.write("Filename: ")
2276 f.write("<span class=\"code\">%s</span><br>\n" % (self.dumpfilename))
2277 dt = datetime.datetime.fromtimestamp(self.reader.header.time_date_stampt)
2278 f.write("Timestamp: %s<br>\n" % dt.strftime('%Y-%m-%d %H:%M:%S'))
2279 self.output_context(f, InspectionWebFormatter.CONTEXT_FULL)
2280 self.output_address_ranges(f)
2281 self.output_footer(f)
2282 return
2283
2284 def output_address_ranges(self, f):
2285 regions = {}
2286 def print_region(_reader, start, size, _location):
2287 regions[start] = size
2288 self.reader.ForEachMemoryRegion(print_region)
2289 f.write("<h3>Available memory regions</h3>\n")
2290 f.write('<div class="code">')
2291 f.write("<table class=\"regions\">\n")
2292 f.write("<thead><tr>")
2293 f.write("<th>Start address</th>")
2294 f.write("<th>End address</th>")
2295 f.write("<th>Number of bytes</th>")
2296 f.write("</tr></thead>\n")
2297 for start in sorted(regions):
2298 size = regions[start]
2299 f.write("<tr>")
2300 f.write("<td>%s</td>" % self.format_address(start))
2301 f.write("<td>&nbsp;%s</td>" % self.format_address(start + size))
2302 f.write("<td>&nbsp;%d</td>" % size)
2303 f.write("</tr>\n")
2304 f.write("</table>\n")
2305 f.write('</div>')
2306 return
2307
2308 def output_module_details(self, f, module):
2309 f.write("<b>%s</b>" % GetModuleName(self.reader, module))
2310 file_version = GetVersionString(module.version_info.dwFileVersionMS,
2311 module.version_info.dwFileVersionLS)
2312 product_version = GetVersionString(module.version_info.dwProductVersionMS,
2313 module.version_info.dwProductVersionLS)
2314 f.write("<br>&nbsp;&nbsp;\n")
2315 f.write("base: %s" % self.reader.FormatIntPtr(module.base_of_image))
2316 f.write("<br>&nbsp;&nbsp;\n")
2317 f.write(" end: %s" % self.reader.FormatIntPtr(module.base_of_image +
2318 module.size_of_image))
2319 f.write("<br>&nbsp;&nbsp;\n")
2320 f.write(" file version: %s" % file_version)
2321 f.write("<br>&nbsp;&nbsp;\n")
2322 f.write(" product version: %s" % product_version)
2323 f.write("<br>&nbsp;&nbsp;\n")
2324 time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
2325 f.write(" timestamp: %s" % time_date_stamp)
2326 f.write("<br>\n");
2327
2328 def output_modules(self, f):
2329 self.output_header(f)
2330 f.write('<div class="code">')
2331 for module in self.reader.module_list.modules:
2332 self.output_module_details(f, module)
2333 f.write("</div>")
2334 self.output_footer(f)
2335 return
2336
2337 def output_context(self, f, details):
2338 exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
2339 f.write("<h3>Exception context</h3>")
2340 f.write('<div class="code">\n')
2341 f.write("Thread id: %d" % exception_thread.id)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00002342 f.write("&nbsp;&nbsp; Exception code: %08X<br/>\n" %
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002343 self.reader.exception.exception.code)
2344 if details == InspectionWebFormatter.CONTEXT_FULL:
2345 if self.reader.exception.exception.parameter_count > 0:
2346 f.write("&nbsp;&nbsp; Exception parameters: \n")
2347 for i in xrange(0, self.reader.exception.exception.parameter_count):
2348 f.write("%08x" % self.reader.exception.exception.information[i])
2349 f.write("<br><br>\n")
2350
2351 for r in CONTEXT_FOR_ARCH[self.reader.arch]:
2352 f.write(HTML_REG_FORMAT %
2353 (r, self.format_address(self.reader.Register(r))))
2354 # TODO(vitalyr): decode eflags.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00002355 if self.reader.arch in [MD_CPU_ARCHITECTURE_ARM, MD_CPU_ARCHITECTURE_ARM64]:
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002356 f.write("<b>cpsr</b>: %s" % bin(self.reader.exception_context.cpsr)[2:])
2357 else:
2358 f.write("<b>eflags</b>: %s" %
2359 bin(self.reader.exception_context.eflags)[2:])
2360 f.write('</div>\n')
2361 return
2362
2363 def align_down(self, a, size):
2364 alignment_correction = a % size
2365 return a - alignment_correction
2366
2367 def align_up(self, a, size):
2368 alignment_correction = (size - 1) - ((a + size - 1) % size)
2369 return a + alignment_correction
2370
2371 def format_object(self, address):
2372 heap_object = self.padawan.SenseObject(address)
2373 return cgi.escape(str(heap_object or ""))
2374
2375 def output_data(self, f, straddress, datakind):
2376 try:
2377 self.output_header(f)
2378 address = int(straddress, 0)
2379 if not self.reader.IsValidAddress(address):
2380 f.write("<h3>Address 0x%x not found in the dump.</h3>" % address)
2381 return
2382 region = self.reader.FindRegion(address)
2383 if datakind == "address":
2384 self.output_words(f, region[0], region[0] + region[1], address, "Dump")
2385 elif datakind == "ascii":
2386 self.output_ascii(f, region[0], region[0] + region[1], address)
2387 self.output_footer(f)
2388
2389 except ValueError:
2390 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
2391 return
2392
2393 def output_words(self, f, start_address, end_address,
2394 highlight_address, desc):
2395 region = self.reader.FindRegion(highlight_address)
2396 if region is None:
2397 f.write("<h3>Address 0x%x not found in the dump.</h3>\n" %
2398 (highlight_address))
2399 return
2400 size = self.heap.PointerSize()
2401 start_address = self.align_down(start_address, size)
2402 low = self.align_down(region[0], size)
2403 high = self.align_up(region[0] + region[1], size)
2404 if start_address < low:
2405 start_address = low
2406 end_address = self.align_up(end_address, size)
2407 if end_address > high:
2408 end_address = high
2409
2410 expand = ""
2411 if start_address != low or end_address != high:
2412 expand = ("(<a href=\"data.html?%s&amp;val=0x%x#highlight\">"
2413 " more..."
2414 " </a>)" %
2415 (self.encfilename, highlight_address))
2416
2417 f.write("<h3>%s 0x%x - 0x%x, "
2418 "highlighting <a href=\"#highlight\">0x%x</a> %s</h3>\n" %
2419 (desc, start_address, end_address, highlight_address, expand))
2420 f.write('<div class="code">')
2421 f.write("<table class=\"codedump\">\n")
2422
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00002423 for j in xrange(0, end_address - start_address, size):
2424 slot = start_address + j
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002425 heap_object = ""
2426 maybe_address = None
2427 end_region = region[0] + region[1]
2428 if slot < region[0] or slot + size > end_region:
2429 straddress = "0x"
2430 for i in xrange(end_region, slot + size):
2431 straddress += "??"
2432 for i in reversed(
2433 xrange(max(slot, region[0]), min(slot + size, end_region))):
2434 straddress += "%02x" % self.reader.ReadU8(i)
2435 for i in xrange(slot, region[0]):
2436 straddress += "??"
2437 else:
2438 maybe_address = self.reader.ReadUIntPtr(slot)
2439 straddress = self.format_address(maybe_address)
2440 if maybe_address:
2441 heap_object = self.format_object(maybe_address)
2442
2443 address_fmt = "%s&nbsp;</td>\n"
2444 if slot == highlight_address:
2445 f.write("<tr class=\"highlight-line\">\n")
2446 address_fmt = "<a id=\"highlight\"></a>%s&nbsp;</td>\n"
2447 elif slot < highlight_address and highlight_address < slot + size:
2448 f.write("<tr class=\"inexact-highlight-line\">\n")
2449 address_fmt = "<a id=\"highlight\"></a>%s&nbsp;</td>\n"
2450 else:
2451 f.write("<tr>\n")
2452
2453 f.write(" <td>")
2454 self.output_comment_box(f, "da-", slot)
2455 f.write("</td>\n")
2456 f.write(" ")
2457 self.td_from_address(f, slot)
2458 f.write(address_fmt % self.format_address(slot))
2459 f.write(" ")
2460 self.td_from_address(f, maybe_address)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00002461 f.write(":&nbsp;%s&nbsp;</td>\n" % straddress)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002462 f.write(" <td>")
2463 if maybe_address != None:
2464 self.output_comment_box(
2465 f, "sv-" + self.reader.FormatIntPtr(slot), maybe_address)
2466 f.write(" </td>\n")
2467 f.write(" <td>%s</td>\n" % (heap_object or ''))
2468 f.write("</tr>\n")
2469 f.write("</table>\n")
2470 f.write("</div>")
2471 return
2472
2473 def output_ascii(self, f, start_address, end_address, highlight_address):
2474 region = self.reader.FindRegion(highlight_address)
2475 if region is None:
2476 f.write("<h3>Address %x not found in the dump.</h3>" %
2477 highlight_address)
2478 return
2479 if start_address < region[0]:
2480 start_address = region[0]
2481 if end_address > region[0] + region[1]:
2482 end_address = region[0] + region[1]
2483
2484 expand = ""
2485 if start_address != region[0] or end_address != region[0] + region[1]:
2486 link = ("data.html?%s&amp;val=0x%x&amp;type=ascii#highlight" %
2487 (self.encfilename, highlight_address))
2488 expand = "(<a href=\"%s\">more...</a>)" % link
2489
2490 f.write("<h3>ASCII dump 0x%x - 0x%x, highlighting 0x%x %s</h3>" %
2491 (start_address, end_address, highlight_address, expand))
2492
2493 line_width = 64
2494
2495 f.write('<div class="code">')
2496
2497 start = self.align_down(start_address, line_width)
2498
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00002499 for i in xrange(end_address - start):
2500 address = start + i
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002501 if address % 64 == 0:
2502 if address != start:
2503 f.write("<br>")
2504 f.write("0x%08x:&nbsp;" % address)
2505 if address < start_address:
2506 f.write("&nbsp;")
2507 else:
2508 if address == highlight_address:
2509 f.write("<span class=\"highlight\">")
2510 code = self.reader.ReadU8(address)
2511 if code < 127 and code >= 32:
2512 f.write("&#")
2513 f.write(str(code))
2514 f.write(";")
2515 else:
2516 f.write("&middot;")
2517 if address == highlight_address:
2518 f.write("</span>")
2519 f.write("</div>")
2520 return
2521
2522 def output_disasm(self, f, straddress, strexact):
2523 try:
2524 self.output_header(f)
2525 address = int(straddress, 0)
2526 if not self.reader.IsValidAddress(address):
2527 f.write("<h3>Address 0x%x not found in the dump.</h3>" % address)
2528 return
2529 region = self.reader.FindRegion(address)
2530 self.output_disasm_range(
2531 f, region[0], region[0] + region[1], address, strexact == "on")
2532 self.output_footer(f)
2533 except ValueError:
2534 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
2535 return
2536
2537 def output_disasm_range(
2538 self, f, start_address, end_address, highlight_address, exact):
2539 region = self.reader.FindRegion(highlight_address)
2540 if start_address < region[0]:
2541 start_address = region[0]
2542 if end_address > region[0] + region[1]:
2543 end_address = region[0] + region[1]
2544 count = end_address - start_address
2545 lines = self.reader.GetDisasmLines(start_address, count)
2546 found = False
2547 if exact:
2548 for line in lines:
2549 if line[0] + start_address == highlight_address:
2550 found = True
2551 break
2552 if not found:
2553 start_address = highlight_address
2554 count = end_address - start_address
2555 lines = self.reader.GetDisasmLines(highlight_address, count)
2556 expand = ""
2557 if start_address != region[0] or end_address != region[0] + region[1]:
2558 exactness = ""
2559 if exact and not found and end_address == region[0] + region[1]:
2560 exactness = "&amp;exact=off"
2561 expand = ("(<a href=\"disasm.html?%s%s"
2562 "&amp;val=0x%x#highlight\">more...</a>)" %
2563 (self.encfilename, exactness, highlight_address))
2564
2565 f.write("<h3>Disassembling 0x%x - 0x%x, highlighting 0x%x %s</h3>" %
2566 (start_address, end_address, highlight_address, expand))
2567 f.write('<div class="code">')
2568 f.write("<table class=\"codedump\">\n");
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00002569 for i in xrange(len(lines)):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002570 line = lines[i]
2571 next_address = count
2572 if i + 1 < len(lines):
2573 next_line = lines[i + 1]
2574 next_address = next_line[0]
2575 self.format_disasm_line(
2576 f, start_address, line, next_address, highlight_address)
2577 f.write("</table>\n")
2578 f.write("</div>")
2579 return
2580
2581 def annotate_disasm_addresses(self, line):
2582 extra = []
2583 for m in ADDRESS_RE.finditer(line):
2584 maybe_address = int(m.group(0), 16)
2585 formatted_address = self.format_address(maybe_address, m.group(0))
2586 line = line.replace(m.group(0), formatted_address)
2587 object_info = self.padawan.SenseObject(maybe_address)
2588 if not object_info:
2589 continue
2590 extra.append(cgi.escape(str(object_info)))
2591 if len(extra) == 0:
2592 return line
2593 return ("%s <span class=\"disasmcomment\">;; %s</span>" %
2594 (line, ", ".join(extra)))
2595
2596 def format_disasm_line(
2597 self, f, start, line, next_address, highlight_address):
2598 line_address = start + line[0]
2599 address_fmt = " <td>%s</td>\n"
2600 if line_address == highlight_address:
2601 f.write("<tr class=\"highlight-line\">\n")
2602 address_fmt = " <td><a id=\"highlight\">%s</a></td>\n"
2603 elif (line_address < highlight_address and
2604 highlight_address < next_address + start):
2605 f.write("<tr class=\"inexact-highlight-line\">\n")
2606 address_fmt = " <td><a id=\"highlight\">%s</a></td>\n"
2607 else:
2608 f.write("<tr>\n")
2609 num_bytes = next_address - line[0]
2610 stack_slot = self.heap.stack_map.get(line_address)
2611 marker = ""
2612 if stack_slot:
2613 marker = "=>"
2614 op_offset = 3 * num_bytes - 1
2615
2616 code = line[1]
2617 # Compute the actual call target which the disassembler is too stupid
2618 # to figure out (it adds the call offset to the disassembly offset rather
2619 # than the absolute instruction address).
2620 if self.heap.reader.arch == MD_CPU_ARCHITECTURE_X86:
2621 if code.startswith("e8"):
2622 words = code.split()
2623 if len(words) > 6 and words[5] == "call":
2624 offset = int(words[4] + words[3] + words[2] + words[1], 16)
2625 target = (line_address + offset + 5) & 0xFFFFFFFF
2626 code = code.replace(words[6], "0x%08x" % target)
2627 # TODO(jkummerow): port this hack to ARM and x64.
2628
2629 opcodes = code[:op_offset]
2630 code = self.annotate_disasm_addresses(code[op_offset:])
2631 f.write(" <td>")
2632 self.output_comment_box(f, "codel-", line_address)
2633 f.write("</td>\n")
2634 f.write(address_fmt % marker)
2635 f.write(" ")
2636 self.td_from_address(f, line_address)
2637 f.write("%s (+0x%x)</td>\n" %
2638 (self.format_address(line_address), line[0]))
2639 f.write(" <td>:&nbsp;%s&nbsp;</td>\n" % opcodes)
2640 f.write(" <td>%s</td>\n" % code)
2641 f.write("</tr>\n")
2642
2643 def output_comment_box(self, f, prefix, address):
2644 f.write("<input type=\"text\" class=\"commentinput\" "
2645 "id=\"%s-address-0x%s\" onchange=\"comment()\" value=\"%s\">" %
2646 (prefix,
2647 self.reader.FormatIntPtr(address),
2648 cgi.escape(self.comments.get_comment(address)) or ""))
2649
2650 MAX_FOUND_RESULTS = 100
2651
2652 def output_find_results(self, f, results):
2653 f.write("Addresses")
2654 toomany = len(results) > self.MAX_FOUND_RESULTS
2655 if toomany:
2656 f.write("(found %i results, displaying only first %i)" %
2657 (len(results), self.MAX_FOUND_RESULTS))
2658 f.write(": \n")
2659 results = sorted(results)
2660 results = results[:min(len(results), self.MAX_FOUND_RESULTS)]
2661 for address in results:
2662 f.write("<span %s>%s</span>\n" %
2663 (self.comments.get_style_class_string(address),
2664 self.format_address(address)))
2665 if toomany:
2666 f.write("...\n")
2667
2668
2669 def output_page_info(self, f, page_kind, page_address, my_page_address):
2670 if my_page_address == page_address and page_address != 0:
2671 f.write("Marked first %s page.\n" % page_kind)
2672 else:
2673 f.write("<span id=\"%spage\" style=\"display:none\">" % page_kind)
2674 f.write("Marked first %s page." % page_kind)
2675 f.write("</span>\n")
2676 f.write("<button onclick=\"onpage('%spage', '0x%x')\">" %
2677 (page_kind, my_page_address))
2678 f.write("Mark as first %s page</button>\n" % page_kind)
2679 return
2680
2681 def output_search_res(self, f, straddress):
2682 try:
2683 self.output_header(f)
2684 f.write("<h3>Search results for %s</h3>" % straddress)
2685
2686 address = int(straddress, 0)
2687
2688 f.write("Comment: ")
2689 self.output_comment_box(f, "search-", address)
2690 f.write("<br>\n")
2691
2692 page_address = address & ~self.heap.PageAlignmentMask()
2693
2694 f.write("Page info: \n")
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00002695 self.output_page_info(f, "old", self.padawan.known_first_old_page, \
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002696 page_address)
2697 self.output_page_info(f, "map", self.padawan.known_first_map_page, \
2698 page_address)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002699
2700 if not self.reader.IsValidAddress(address):
2701 f.write("<h3>The contents at address %s not found in the dump.</h3>" % \
2702 straddress)
2703 else:
2704 # Print as words
2705 self.output_words(f, address - 8, address + 32, address, "Dump")
2706
2707 # Print as ASCII
2708 f.write("<hr>\n")
2709 self.output_ascii(f, address, address + 256, address)
2710
2711 # Print as code
2712 f.write("<hr>\n")
2713 self.output_disasm_range(f, address - 16, address + 16, address, True)
2714
2715 aligned_res, unaligned_res = self.reader.FindWordList(address)
2716
2717 if len(aligned_res) > 0:
2718 f.write("<h3>Occurrences of 0x%x at aligned addresses</h3>\n" %
2719 address)
2720 self.output_find_results(f, aligned_res)
2721
2722 if len(unaligned_res) > 0:
2723 f.write("<h3>Occurrences of 0x%x at unaligned addresses</h3>\n" % \
2724 address)
2725 self.output_find_results(f, unaligned_res)
2726
2727 if len(aligned_res) + len(unaligned_res) == 0:
2728 f.write("<h3>No occurences of 0x%x found in the dump</h3>\n" % address)
2729
2730 self.output_footer(f)
2731
2732 except ValueError:
2733 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
2734 return
2735
2736 def output_disasm_pc(self, f):
2737 address = self.reader.ExceptionIP()
2738 if not self.reader.IsValidAddress(address):
2739 return
2740 self.output_disasm_range(f, address - 16, address + 16, address, True)
2741
2742
2743WEB_DUMPS_HEADER = """
2744<!DOCTYPE html>
2745<html>
2746<head>
2747<meta content="text/html; charset=utf-8" http-equiv="content-type">
2748<style media="screen" type="text/css">
2749
2750.dumplist {
2751 border-collapse : collapse;
2752 border-spacing : 0px;
2753 font-family: monospace;
2754}
2755
2756.dumpcomments {
2757 border : 1px solid LightGray;
2758 width : 32em;
2759}
2760
2761</style>
2762
2763<script type="application/javascript">
2764
2765var dump_str = "dump-";
2766var dump_len = dump_str.length;
2767
2768function dump_comment() {
2769 var s = event.srcElement.id;
2770 var index = s.indexOf(dump_str);
2771 if (index >= 0) {
2772 send_dump_desc(s.substring(index + dump_len), event.srcElement.value);
2773 }
2774}
2775
2776function send_dump_desc(name, desc) {
2777 xmlhttp = new XMLHttpRequest();
2778 name = encodeURIComponent(name)
2779 desc = encodeURIComponent(desc)
2780 xmlhttp.open("GET",
2781 "setdumpdesc?dump=" + name +
2782 "&description=" + desc, true);
2783 xmlhttp.send();
2784}
2785
2786</script>
2787
2788<title>Dump list</title>
2789</head>
2790
2791<body>
2792"""
2793
2794WEB_DUMPS_FOOTER = """
2795</body>
2796</html>
2797"""
2798
2799DUMP_FILE_RE = re.compile(r"[-_0-9a-zA-Z][-\._0-9a-zA-Z]*\.dmp$")
2800
2801
2802class InspectionWebServer(BaseHTTPServer.HTTPServer):
2803 def __init__(self, port_number, switches, minidump_name):
2804 BaseHTTPServer.HTTPServer.__init__(
2805 self, ('', port_number), InspectionWebHandler)
2806 splitpath = os.path.split(minidump_name)
2807 self.dumppath = splitpath[0]
2808 self.dumpfilename = splitpath[1]
2809 self.default_formatter = InspectionWebFormatter(
2810 switches, minidump_name, self)
2811 self.formatters = { self.dumpfilename : self.default_formatter }
2812 self.switches = switches
2813
2814 def output_dump_desc_field(self, f, name):
2815 try:
2816 descfile = open(os.path.join(self.dumppath, name + ".desc"), "r")
2817 desc = descfile.readline()
2818 descfile.close()
2819 except IOError:
2820 desc = ""
2821 f.write("<input type=\"text\" class=\"dumpcomments\" "
2822 "id=\"dump-%s\" onchange=\"dump_comment()\" value=\"%s\">\n" %
2823 (cgi.escape(name), desc))
2824
2825 def set_dump_desc(self, name, description):
2826 if not DUMP_FILE_RE.match(name):
2827 return False
2828 fname = os.path.join(self.dumppath, name)
2829 if not os.path.isfile(fname):
2830 return False
2831 fname = fname + ".desc"
2832 descfile = open(fname, "w")
2833 descfile.write(description)
2834 descfile.close()
2835 return True
2836
2837 def get_dump_formatter(self, name):
2838 if name is None:
2839 return self.default_formatter
2840 else:
2841 if not DUMP_FILE_RE.match(name):
2842 raise WebParameterError("Invalid name '%s'" % name)
2843 formatter = self.formatters.get(name, None)
2844 if formatter is None:
2845 try:
2846 formatter = InspectionWebFormatter(
2847 self.switches, os.path.join(self.dumppath, name), self)
2848 self.formatters[name] = formatter
2849 except IOError:
2850 raise WebParameterError("Could not open dump '%s'" % name)
2851 return formatter
2852
2853 def output_dumps(self, f):
2854 f.write(WEB_DUMPS_HEADER)
2855 f.write("<h3>List of available dumps</h3>")
2856 f.write("<table class=\"dumplist\">\n")
2857 f.write("<thead><tr>")
2858 f.write("<th>Name</th>")
2859 f.write("<th>File time</th>")
2860 f.write("<th>Comment</th>")
2861 f.write("</tr></thead>")
2862 dumps_by_time = {}
2863 for fname in os.listdir(self.dumppath):
2864 if DUMP_FILE_RE.match(fname):
2865 mtime = os.stat(os.path.join(self.dumppath, fname)).st_mtime
2866 fnames = dumps_by_time.get(mtime, [])
2867 fnames.append(fname)
2868 dumps_by_time[mtime] = fnames
2869
2870 for mtime in sorted(dumps_by_time, reverse=True):
2871 fnames = dumps_by_time[mtime]
2872 for fname in fnames:
2873 f.write("<tr>\n")
2874 f.write("<td><a href=\"summary.html?%s\">%s</a></td>\n" % (
2875 (urllib.urlencode({ 'dump' : fname }), fname)))
2876 f.write("<td>&nbsp;&nbsp;&nbsp;")
2877 f.write(datetime.datetime.fromtimestamp(mtime))
2878 f.write("</td>")
2879 f.write("<td>&nbsp;&nbsp;&nbsp;")
2880 self.output_dump_desc_field(f, fname)
2881 f.write("</td>")
2882 f.write("</tr>\n")
2883 f.write("</table>\n")
2884 f.write(WEB_DUMPS_FOOTER)
2885 return
2886
2887class InspectionShell(cmd.Cmd):
2888 def __init__(self, reader, heap):
2889 cmd.Cmd.__init__(self)
2890 self.reader = reader
2891 self.heap = heap
2892 self.padawan = InspectionPadawan(reader, heap)
2893 self.prompt = "(grok) "
2894
2895 def do_da(self, address):
2896 """
2897 Print ASCII string starting at specified address.
2898 """
2899 address = int(address, 16)
2900 string = ""
2901 while self.reader.IsValidAddress(address):
2902 code = self.reader.ReadU8(address)
2903 if code < 128:
2904 string += chr(code)
2905 else:
2906 break
2907 address += 1
2908 if string == "":
2909 print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address)
2910 else:
2911 print "%s\n" % string
2912
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00002913 def do_dd(self, args):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002914 """
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00002915 Interpret memory in the given region [address, address + num * word_size)
2916 (if available) as a sequence of words. Automatic alignment is not performed.
2917 If the num is not specified, a default value of 16 words is used.
2918 Synopsis: dd 0x<address> 0x<num>
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002919 """
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00002920 args = args.split(' ')
2921 start = int(args[0], 16)
2922 num = int(args[1], 16) if len(args) > 1 else 0x10
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002923 if (start & self.heap.ObjectAlignmentMask()) != 0:
2924 print "Warning: Dumping un-aligned memory, is this what you had in mind?"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00002925 for i in xrange(0,
2926 self.reader.PointerSize() * num,
2927 self.reader.PointerSize()):
2928 slot = start + i
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002929 if not self.reader.IsValidAddress(slot):
2930 print "Address is not contained within the minidump!"
2931 return
2932 maybe_address = self.reader.ReadUIntPtr(slot)
2933 heap_object = self.padawan.SenseObject(maybe_address)
2934 print "%s: %s %s" % (self.reader.FormatIntPtr(slot),
2935 self.reader.FormatIntPtr(maybe_address),
2936 heap_object or '')
2937
2938 def do_do(self, address):
2939 """
2940 Interpret memory at the given address as a V8 object. Automatic
2941 alignment makes sure that you can pass tagged as well as un-tagged
2942 addresses.
2943 """
2944 address = int(address, 16)
2945 if (address & self.heap.ObjectAlignmentMask()) == 0:
2946 address = address + 1
2947 elif (address & self.heap.ObjectAlignmentMask()) != 1:
2948 print "Address doesn't look like a valid pointer!"
2949 return
2950 heap_object = self.padawan.SenseObject(address)
2951 if heap_object:
2952 heap_object.Print(Printer())
2953 else:
2954 print "Address cannot be interpreted as object!"
2955
2956 def do_do_desc(self, address):
2957 """
2958 Print a descriptor array in a readable format.
2959 """
2960 start = int(address, 16)
2961 if ((start & 1) == 1): start = start - 1
2962 DescriptorArray(FixedArray(self.heap, None, start)).Print(Printer())
2963
2964 def do_do_map(self, address):
2965 """
2966 Print a descriptor array in a readable format.
2967 """
2968 start = int(address, 16)
2969 if ((start & 1) == 1): start = start - 1
2970 Map(self.heap, None, start).Print(Printer())
2971
2972 def do_do_trans(self, address):
2973 """
2974 Print a transition array in a readable format.
2975 """
2976 start = int(address, 16)
2977 if ((start & 1) == 1): start = start - 1
2978 TransitionArray(FixedArray(self.heap, None, start)).Print(Printer())
2979
2980 def do_dp(self, address):
2981 """
2982 Interpret memory at the given address as being on a V8 heap page
2983 and print information about the page header (if available).
2984 """
2985 address = int(address, 16)
2986 page_address = address & ~self.heap.PageAlignmentMask()
2987 if self.reader.IsValidAddress(page_address):
2988 raise NotImplementedError
2989 else:
2990 print "Page header is not available!"
2991
2992 def do_k(self, arguments):
2993 """
2994 Teach V8 heap layout information to the inspector. This increases
2995 the amount of annotations the inspector can produce while dumping
2996 data. The first page of each heap space is of particular interest
2997 because it contains known objects that do not move.
2998 """
2999 self.padawan.PrintKnowledge()
3000
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00003001 def do_ko(self, address):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003002 """
3003 Teach V8 heap layout information to the inspector. Set the first
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00003004 old space page by passing any pointer into that page.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003005 """
3006 address = int(address, 16)
3007 page_address = address & ~self.heap.PageAlignmentMask()
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00003008 self.padawan.known_first_old_page = page_address
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003009
3010 def do_km(self, address):
3011 """
3012 Teach V8 heap layout information to the inspector. Set the first
3013 map-space page by passing any pointer into that page.
3014 """
3015 address = int(address, 16)
3016 page_address = address & ~self.heap.PageAlignmentMask()
3017 self.padawan.known_first_map_page = page_address
3018
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003019 def do_list(self, smth):
3020 """
3021 List all available memory regions.
3022 """
3023 def print_region(reader, start, size, location):
3024 print " %s - %s (%d bytes)" % (reader.FormatIntPtr(start),
3025 reader.FormatIntPtr(start + size),
3026 size)
3027 print "Available memory regions:"
3028 self.reader.ForEachMemoryRegion(print_region)
3029
3030 def do_lm(self, arg):
3031 """
3032 List details for all loaded modules in the minidump. An argument can
3033 be passed to limit the output to only those modules that contain the
3034 argument as a substring (case insensitive match).
3035 """
3036 for module in self.reader.module_list.modules:
3037 if arg:
3038 name = GetModuleName(self.reader, module).lower()
3039 if name.find(arg.lower()) >= 0:
3040 PrintModuleDetails(self.reader, module)
3041 else:
3042 PrintModuleDetails(self.reader, module)
3043 print
3044
3045 def do_s(self, word):
3046 """
3047 Search for a given word in available memory regions. The given word
3048 is expanded to full pointer size and searched at aligned as well as
3049 un-aligned memory locations. Use 'sa' to search aligned locations
3050 only.
3051 """
3052 try:
3053 word = int(word, 0)
3054 except ValueError:
3055 print "Malformed word, prefix with '0x' to use hexadecimal format."
3056 return
3057 print "Searching for word %d/0x%s:" % (word, self.reader.FormatIntPtr(word))
3058 self.reader.FindWord(word)
3059
3060 def do_sh(self, none):
3061 """
3062 Search for the V8 Heap object in all available memory regions. You
3063 might get lucky and find this rare treasure full of invaluable
3064 information.
3065 """
3066 raise NotImplementedError
3067
3068 def do_u(self, args):
3069 """
3070 Unassemble memory in the region [address, address + size). If the
3071 size is not specified, a default value of 32 bytes is used.
3072 Synopsis: u 0x<address> 0x<size>
3073 """
3074 args = args.split(' ')
3075 start = int(args[0], 16)
3076 size = int(args[1], 16) if len(args) > 1 else 0x20
3077 if not self.reader.IsValidAddress(start):
3078 print "Address is not contained within the minidump!"
3079 return
3080 lines = self.reader.GetDisasmLines(start, size)
3081 for line in lines:
3082 print FormatDisasmLine(start, self.heap, line)
3083 print
3084
3085 def do_EOF(self, none):
3086 raise KeyboardInterrupt
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003087
3088EIP_PROXIMITY = 64
3089
Ben Murdoch3ef787d2012-04-12 10:51:47 +01003090CONTEXT_FOR_ARCH = {
3091 MD_CPU_ARCHITECTURE_AMD64:
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003092 ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip',
3093 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'],
3094 MD_CPU_ARCHITECTURE_ARM:
3095 ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
3096 'r10', 'r11', 'r12', 'sp', 'lr', 'pc'],
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00003097 MD_CPU_ARCHITECTURE_ARM64:
3098 ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
3099 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 'r16', 'r17', 'r18', 'r19',
3100 'r20', 'r21', 'r22', 'r23', 'r24', 'r25', 'r26', 'r27', 'r28',
3101 'fp', 'lr', 'sp', 'pc'],
Ben Murdoch3ef787d2012-04-12 10:51:47 +01003102 MD_CPU_ARCHITECTURE_X86:
3103 ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
3104}
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003105
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003106KNOWN_MODULES = {'chrome.exe', 'chrome.dll'}
3107
3108def GetVersionString(ms, ls):
3109 return "%d.%d.%d.%d" % (ms >> 16, ms & 0xffff, ls >> 16, ls & 0xffff)
3110
3111
3112def GetModuleName(reader, module):
3113 name = reader.ReadMinidumpString(module.module_name_rva)
3114 # simplify for path manipulation
3115 name = name.encode('utf-8')
3116 return str(os.path.basename(str(name).replace("\\", "/")))
3117
3118
3119def PrintModuleDetails(reader, module):
3120 print "%s" % GetModuleName(reader, module)
3121 file_version = GetVersionString(module.version_info.dwFileVersionMS,
3122 module.version_info.dwFileVersionLS)
3123 product_version = GetVersionString(module.version_info.dwProductVersionMS,
3124 module.version_info.dwProductVersionLS)
3125 print " base: %s" % reader.FormatIntPtr(module.base_of_image)
3126 print " end: %s" % reader.FormatIntPtr(module.base_of_image +
3127 module.size_of_image)
3128 print " file version: %s" % file_version
3129 print " product version: %s" % product_version
3130 time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
3131 print " timestamp: %s" % time_date_stamp
3132
3133
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003134def AnalyzeMinidump(options, minidump_name):
3135 reader = MinidumpReader(options, minidump_name)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003136 heap = None
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003137 DebugPrint("========================================")
3138 if reader.exception is None:
3139 print "Minidump has no exception info"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003140 else:
3141 print "Exception info:"
3142 exception_thread = reader.thread_map[reader.exception.thread_id]
3143 print " thread id: %d" % exception_thread.id
3144 print " code: %08X" % reader.exception.exception.code
3145 print " context:"
3146 for r in CONTEXT_FOR_ARCH[reader.arch]:
3147 print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r)))
3148 # TODO(vitalyr): decode eflags.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00003149 if reader.arch in [MD_CPU_ARCHITECTURE_ARM, MD_CPU_ARCHITECTURE_ARM64]:
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003150 print " cpsr: %s" % bin(reader.exception_context.cpsr)[2:]
3151 else:
3152 print " eflags: %s" % bin(reader.exception_context.eflags)[2:]
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003153
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003154 print
3155 print " modules:"
3156 for module in reader.module_list.modules:
3157 name = GetModuleName(reader, module)
3158 if name in KNOWN_MODULES:
3159 print " %s at %08X" % (name, module.base_of_image)
3160 reader.TryLoadSymbolsFor(name, module)
3161 print
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003162
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003163 stack_top = reader.ExceptionSP()
3164 stack_bottom = exception_thread.stack.start + \
3165 exception_thread.stack.memory.data_size
3166 stack_map = {reader.ExceptionIP(): -1}
3167 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
3168 maybe_address = reader.ReadUIntPtr(slot)
3169 if not maybe_address in stack_map:
3170 stack_map[maybe_address] = slot
3171 heap = V8Heap(reader, stack_map)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003172
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003173 print "Disassembly around exception.eip:"
3174 eip_symbol = reader.FindSymbol(reader.ExceptionIP())
3175 if eip_symbol is not None:
3176 print eip_symbol
3177 disasm_start = reader.ExceptionIP() - EIP_PROXIMITY
3178 disasm_bytes = 2 * EIP_PROXIMITY
3179 if (options.full):
3180 full_range = reader.FindRegion(reader.ExceptionIP())
3181 if full_range is not None:
3182 disasm_start = full_range[0]
3183 disasm_bytes = full_range[1]
3184
3185 lines = reader.GetDisasmLines(disasm_start, disasm_bytes)
3186
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00003187 if not lines:
3188 print "Could not disassemble using %s." % OBJDUMP_BIN
3189 print "Pass path to architecture specific objdump via --objdump?"
3190
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003191 for line in lines:
3192 print FormatDisasmLine(disasm_start, heap, line)
3193 print
3194
3195 if heap is None:
3196 heap = V8Heap(reader, None)
3197
3198 if options.full:
3199 FullDump(reader, heap)
3200
3201 if options.command:
3202 InspectionShell(reader, heap).onecmd(options.command)
3203
3204 if options.shell:
3205 try:
3206 InspectionShell(reader, heap).cmdloop("type help to get help")
3207 except KeyboardInterrupt:
3208 print "Kthxbye."
3209 elif not options.command:
3210 if reader.exception is not None:
3211 frame_pointer = reader.ExceptionFP()
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00003212 in_oom_dump_area = False
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003213 print "Annotated stack (from exception.esp to bottom):"
3214 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
3215 ascii_content = [c if c >= '\x20' and c < '\x7f' else '.'
3216 for c in reader.ReadBytes(slot, reader.PointerSize())]
3217 maybe_address = reader.ReadUIntPtr(slot)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00003218 maybe_address_contents = None
3219 if maybe_address >= stack_top and maybe_address <= stack_bottom:
3220 maybe_address_contents = reader.ReadUIntPtr(maybe_address)
3221 if maybe_address_contents == 0xdecade00:
3222 in_oom_dump_area = True
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003223 heap_object = heap.FindObject(maybe_address)
3224 maybe_symbol = reader.FindSymbol(maybe_address)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00003225 oom_comment = ""
3226 if in_oom_dump_area:
3227 if maybe_address_contents == 0xdecade00:
3228 oom_comment = " <----- HeapStats start marker"
3229 elif maybe_address_contents == 0xdecade01:
3230 oom_comment = " <----- HeapStats end marker"
3231 elif maybe_address_contents is not None:
3232 oom_comment = " %d (%d Mbytes)" % (maybe_address_contents,
3233 maybe_address_contents >> 20)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003234 if slot == frame_pointer:
3235 maybe_symbol = "<---- frame pointer"
3236 frame_pointer = maybe_address
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00003237 print "%s: %s %s %s%s" % (reader.FormatIntPtr(slot),
3238 reader.FormatIntPtr(maybe_address),
3239 "".join(ascii_content),
3240 maybe_symbol or "",
3241 oom_comment)
3242 if maybe_address_contents == 0xdecade01:
3243 in_oom_dump_area = False
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003244 if heap_object:
3245 heap_object.Print(Printer())
3246 print
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003247
3248 reader.Dispose()
3249
3250
3251if __name__ == "__main__":
3252 parser = optparse.OptionParser(USAGE)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003253 parser.add_option("-s", "--shell", dest="shell", action="store_true",
3254 help="start an interactive inspector shell")
3255 parser.add_option("-w", "--web", dest="web", action="store_true",
3256 help="start a web server on localhost:%i" % PORT_NUMBER)
3257 parser.add_option("-c", "--command", dest="command", default="",
3258 help="run an interactive inspector shell command and exit")
3259 parser.add_option("-f", "--full", dest="full", action="store_true",
3260 help="dump all information contained in the minidump")
3261 parser.add_option("--symdir", dest="symdir", default=".",
3262 help="directory containing *.pdb.sym file with symbols")
3263 parser.add_option("--objdump",
3264 default="/usr/bin/objdump",
3265 help="objdump tool to use [default: %default]")
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003266 options, args = parser.parse_args()
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003267 if os.path.exists(options.objdump):
3268 disasm.OBJDUMP_BIN = options.objdump
3269 OBJDUMP_BIN = options.objdump
3270 else:
3271 print "Cannot find %s, falling back to default objdump" % options.objdump
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003272 if len(args) != 1:
3273 parser.print_help()
3274 sys.exit(1)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003275 if options.web:
3276 try:
3277 server = InspectionWebServer(PORT_NUMBER, options, args[0])
3278 print 'Started httpserver on port ' , PORT_NUMBER
3279 webbrowser.open('http://localhost:%i/summary.html' % PORT_NUMBER)
3280 server.serve_forever()
3281 except KeyboardInterrupt:
3282 print '^C received, shutting down the web server'
3283 server.socket.close()
3284 else:
3285 AnalyzeMinidump(options, args[0])