blob: 4525e7ef33e703974b8b610cdbd13c4a2c6668f4 [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 Murdochf3b273f2017-01-17 12:11:28 +000042import StringIO
Ben Murdoche0cee9b2011-05-25 10:26:03 +010043import sys
44import types
Ben Murdochb8a8cc12014-11-26 15:28:44 +000045import urllib
46import urlparse
47import v8heapconst
48import webbrowser
49
50PORT_NUMBER = 8081
Ben Murdoche0cee9b2011-05-25 10:26:03 +010051
52
Ben Murdochb8a8cc12014-11-26 15:28:44 +000053USAGE="""usage: %prog [OPTIONS] [DUMP-FILE]
Ben Murdoche0cee9b2011-05-25 10:26:03 +010054
55Minidump analyzer.
56
57Shows the processor state at the point of exception including the
58stack of the active thread and the referenced objects in the V8
59heap. Code objects are disassembled and the addresses linked from the
Ben Murdochb8a8cc12014-11-26 15:28:44 +000060stack (e.g. pushed return addresses) are marked with "=>".
Ben Murdoche0cee9b2011-05-25 10:26:03 +010061
62Examples:
Ben Murdochb8a8cc12014-11-26 15:28:44 +000063 $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp"""
Ben Murdoche0cee9b2011-05-25 10:26:03 +010064
Ben Murdoch3ef787d2012-04-12 10:51:47 +010065
Ben Murdoche0cee9b2011-05-25 10:26:03 +010066DEBUG=False
67
68
69def DebugPrint(s):
70 if not DEBUG: return
71 print s
72
73
74class Descriptor(object):
75 """Descriptor of a structure in a memory."""
76
77 def __init__(self, fields):
78 self.fields = fields
79 self.is_flexible = False
80 for _, type_or_func in fields:
81 if isinstance(type_or_func, types.FunctionType):
82 self.is_flexible = True
83 break
84 if not self.is_flexible:
85 self.ctype = Descriptor._GetCtype(fields)
86 self.size = ctypes.sizeof(self.ctype)
87
88 def Read(self, memory, offset):
89 if self.is_flexible:
90 fields_copy = self.fields[:]
91 last = 0
92 for name, type_or_func in fields_copy:
93 if isinstance(type_or_func, types.FunctionType):
94 partial_ctype = Descriptor._GetCtype(fields_copy[:last])
95 partial_object = partial_ctype.from_buffer(memory, offset)
96 type = type_or_func(partial_object)
97 if type is not None:
98 fields_copy[last] = (name, type)
99 last += 1
100 else:
101 last += 1
102 complete_ctype = Descriptor._GetCtype(fields_copy[:last])
103 else:
104 complete_ctype = self.ctype
105 return complete_ctype.from_buffer(memory, offset)
106
107 @staticmethod
108 def _GetCtype(fields):
109 class Raw(ctypes.Structure):
110 _fields_ = fields
111 _pack_ = 1
112
113 def __str__(self):
114 return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field))
115 for field, _ in Raw._fields_) + "}"
116 return Raw
117
118
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000119def FullDump(reader, heap):
120 """Dump all available memory regions."""
121 def dump_region(reader, start, size, location):
122 print
123 while start & 3 != 0:
124 start += 1
125 size -= 1
126 location += 1
127 is_executable = reader.IsProbableExecutableRegion(location, size)
128 is_ascii = reader.IsProbableASCIIRegion(location, size)
129
130 if is_executable is not False:
131 lines = reader.GetDisasmLines(start, size)
132 for line in lines:
133 print FormatDisasmLine(start, heap, line)
134 print
135
136 if is_ascii is not False:
137 # Output in the same format as the Unix hd command
138 addr = start
Ben Murdoch014dc512016-03-22 12:00:34 +0000139 for i in xrange(0, size, 16):
140 slot = i + location
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000141 hex_line = ""
142 asc_line = ""
Ben Murdoch014dc512016-03-22 12:00:34 +0000143 for i in xrange(16):
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000144 if slot + i < location + size:
145 byte = ctypes.c_uint8.from_buffer(reader.minidump, slot + i).value
146 if byte >= 0x20 and byte < 0x7f:
147 asc_line += chr(byte)
148 else:
149 asc_line += "."
150 hex_line += " %02x" % (byte)
151 else:
152 hex_line += " "
153 if i == 7:
154 hex_line += " "
155 print "%s %s |%s|" % (reader.FormatIntPtr(addr),
156 hex_line,
157 asc_line)
158 addr += 16
159
160 if is_executable is not True and is_ascii is not True:
161 print "%s - %s" % (reader.FormatIntPtr(start),
162 reader.FormatIntPtr(start + size))
Ben Murdoch014dc512016-03-22 12:00:34 +0000163 print start + size + 1;
164 for i in xrange(0, size, reader.PointerSize()):
165 slot = start + i
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000166 maybe_address = reader.ReadUIntPtr(slot)
167 heap_object = heap.FindObject(maybe_address)
168 print "%s: %s" % (reader.FormatIntPtr(slot),
169 reader.FormatIntPtr(maybe_address))
170 if heap_object:
171 heap_object.Print(Printer())
172 print
173
174 reader.ForEachMemoryRegion(dump_region)
175
176# Heap constants generated by 'make grokdump' in v8heapconst module.
177INSTANCE_TYPES = v8heapconst.INSTANCE_TYPES
178KNOWN_MAPS = v8heapconst.KNOWN_MAPS
179KNOWN_OBJECTS = v8heapconst.KNOWN_OBJECTS
180
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100181# Set of structures and constants that describe the layout of minidump
182# files. Based on MSDN and Google Breakpad.
183
184MINIDUMP_HEADER = Descriptor([
185 ("signature", ctypes.c_uint32),
186 ("version", ctypes.c_uint32),
187 ("stream_count", ctypes.c_uint32),
188 ("stream_directories_rva", ctypes.c_uint32),
189 ("checksum", ctypes.c_uint32),
190 ("time_date_stampt", ctypes.c_uint32),
191 ("flags", ctypes.c_uint64)
192])
193
194MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
195 ("data_size", ctypes.c_uint32),
196 ("rva", ctypes.c_uint32)
197])
198
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000199MINIDUMP_STRING = Descriptor([
200 ("length", ctypes.c_uint32),
201 ("buffer", lambda t: ctypes.c_uint8 * (t.length + 2))
202])
203
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100204MINIDUMP_DIRECTORY = Descriptor([
205 ("stream_type", ctypes.c_uint32),
206 ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
207])
208
209MD_EXCEPTION_MAXIMUM_PARAMETERS = 15
210
211MINIDUMP_EXCEPTION = Descriptor([
212 ("code", ctypes.c_uint32),
213 ("flags", ctypes.c_uint32),
214 ("record", ctypes.c_uint64),
215 ("address", ctypes.c_uint64),
216 ("parameter_count", ctypes.c_uint32),
217 ("unused_alignment", ctypes.c_uint32),
218 ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS)
219])
220
221MINIDUMP_EXCEPTION_STREAM = Descriptor([
222 ("thread_id", ctypes.c_uint32),
223 ("unused_alignment", ctypes.c_uint32),
224 ("exception", MINIDUMP_EXCEPTION.ctype),
225 ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
226])
227
228# Stream types.
229MD_UNUSED_STREAM = 0
230MD_RESERVED_STREAM_0 = 1
231MD_RESERVED_STREAM_1 = 2
232MD_THREAD_LIST_STREAM = 3
233MD_MODULE_LIST_STREAM = 4
234MD_MEMORY_LIST_STREAM = 5
235MD_EXCEPTION_STREAM = 6
236MD_SYSTEM_INFO_STREAM = 7
237MD_THREAD_EX_LIST_STREAM = 8
238MD_MEMORY_64_LIST_STREAM = 9
239MD_COMMENT_STREAM_A = 10
240MD_COMMENT_STREAM_W = 11
241MD_HANDLE_DATA_STREAM = 12
242MD_FUNCTION_TABLE_STREAM = 13
243MD_UNLOADED_MODULE_LIST_STREAM = 14
244MD_MISC_INFO_STREAM = 15
245MD_MEMORY_INFO_LIST_STREAM = 16
246MD_THREAD_INFO_LIST_STREAM = 17
247MD_HANDLE_OPERATION_LIST_STREAM = 18
248
249MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80
250
251MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([
252 ("control_word", ctypes.c_uint32),
253 ("status_word", ctypes.c_uint32),
254 ("tag_word", ctypes.c_uint32),
255 ("error_offset", ctypes.c_uint32),
256 ("error_selector", ctypes.c_uint32),
257 ("data_offset", ctypes.c_uint32),
258 ("data_selector", ctypes.c_uint32),
259 ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE),
260 ("cr0_npx_state", ctypes.c_uint32)
261])
262
263MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512
264
265# Context flags.
266MD_CONTEXT_X86 = 0x00010000
267MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001)
268MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002)
269MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004)
270MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008)
271MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010)
272MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020)
273
274def EnableOnFlag(type, flag):
275 return lambda o: [None, type][int((o.context_flags & flag) != 0)]
276
277MINIDUMP_CONTEXT_X86 = Descriptor([
278 ("context_flags", ctypes.c_uint32),
279 # MD_CONTEXT_X86_DEBUG_REGISTERS.
280 ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
281 ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
282 ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
283 ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
284 ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
285 ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
286 # MD_CONTEXT_X86_FLOATING_POINT.
287 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype,
288 MD_CONTEXT_X86_FLOATING_POINT)),
289 # MD_CONTEXT_X86_SEGMENTS.
290 ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
291 ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
292 ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
293 ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
294 # MD_CONTEXT_X86_INTEGER.
295 ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
296 ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
297 ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
298 ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
299 ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
300 ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
301 # MD_CONTEXT_X86_CONTROL.
302 ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
303 ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
304 ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
305 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
306 ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
307 ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
308 # MD_CONTEXT_X86_EXTENDED_REGISTERS.
309 ("extended_registers",
310 EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE,
311 MD_CONTEXT_X86_EXTENDED_REGISTERS))
312])
313
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000314MD_CONTEXT_ARM = 0x40000000
315MD_CONTEXT_ARM_INTEGER = (MD_CONTEXT_ARM | 0x00000002)
316MD_CONTEXT_ARM_FLOATING_POINT = (MD_CONTEXT_ARM | 0x00000004)
317MD_FLOATINGSAVEAREA_ARM_FPR_COUNT = 32
318MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT = 8
319
320MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([
321 ("fpscr", ctypes.c_uint64),
322 ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPR_COUNT),
323 ("extra", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT)
324])
325
326MINIDUMP_CONTEXT_ARM = Descriptor([
327 ("context_flags", ctypes.c_uint32),
328 # MD_CONTEXT_ARM_INTEGER.
329 ("r0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
330 ("r1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
331 ("r2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
332 ("r3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
333 ("r4", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
334 ("r5", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
335 ("r6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
336 ("r7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
337 ("r8", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
338 ("r9", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
339 ("r10", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
340 ("r11", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
341 ("r12", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
342 ("sp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
343 ("lr", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
344 ("pc", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
345 ("cpsr", ctypes.c_uint32),
346 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype,
347 MD_CONTEXT_ARM_FLOATING_POINT))
348])
349
Ben Murdoch014dc512016-03-22 12:00:34 +0000350
351MD_CONTEXT_ARM64 = 0x80000000
352MD_CONTEXT_ARM64_INTEGER = (MD_CONTEXT_ARM64 | 0x00000002)
353MD_CONTEXT_ARM64_FLOATING_POINT = (MD_CONTEXT_ARM64 | 0x00000004)
354MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT = 64
355
356MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([
357 ("fpscr", ctypes.c_uint64),
358 ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT),
359])
360
361MINIDUMP_CONTEXT_ARM64 = Descriptor([
362 ("context_flags", ctypes.c_uint64),
363 # MD_CONTEXT_ARM64_INTEGER.
364 ("r0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
365 ("r1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
366 ("r2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
367 ("r3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
368 ("r4", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
369 ("r5", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
370 ("r6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
371 ("r7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
372 ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
373 ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
374 ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
375 ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
376 ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
377 ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
378 ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
379 ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
380 ("r16", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
381 ("r17", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
382 ("r18", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
383 ("r19", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
384 ("r20", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
385 ("r21", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
386 ("r22", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
387 ("r23", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
388 ("r24", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
389 ("r25", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
390 ("r26", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
391 ("r27", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
392 ("r28", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
393 ("fp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
394 ("lr", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
395 ("sp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
396 ("pc", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
397 ("cpsr", ctypes.c_uint32),
398 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype,
399 MD_CONTEXT_ARM64_FLOATING_POINT))
400])
401
402
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100403MD_CONTEXT_AMD64 = 0x00100000
404MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001)
405MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002)
406MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004)
407MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008)
408MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010)
409
410MINIDUMP_CONTEXT_AMD64 = Descriptor([
411 ("p1_home", ctypes.c_uint64),
412 ("p2_home", ctypes.c_uint64),
413 ("p3_home", ctypes.c_uint64),
414 ("p4_home", ctypes.c_uint64),
415 ("p5_home", ctypes.c_uint64),
416 ("p6_home", ctypes.c_uint64),
417 ("context_flags", ctypes.c_uint32),
418 ("mx_csr", ctypes.c_uint32),
419 # MD_CONTEXT_AMD64_CONTROL.
420 ("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
421 # MD_CONTEXT_AMD64_SEGMENTS
422 ("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
423 ("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
424 ("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
425 ("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
426 # MD_CONTEXT_AMD64_CONTROL.
427 ("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
428 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)),
429 # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
430 ("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
431 ("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
432 ("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
433 ("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
434 ("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
435 ("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
436 # MD_CONTEXT_AMD64_INTEGER.
437 ("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
438 ("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
439 ("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
440 ("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
441 # MD_CONTEXT_AMD64_CONTROL.
442 ("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
443 # MD_CONTEXT_AMD64_INTEGER.
444 ("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
445 ("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
446 ("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
447 ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
448 ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
449 ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
450 ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
451 ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
452 ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
453 ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
454 ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
455 # MD_CONTEXT_AMD64_CONTROL.
456 ("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
457 # MD_CONTEXT_AMD64_FLOATING_POINT
458 ("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
459 MD_CONTEXT_AMD64_FLOATING_POINT)),
460 ("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
461 MD_CONTEXT_AMD64_FLOATING_POINT)),
462 ("vector_control", EnableOnFlag(ctypes.c_uint64,
463 MD_CONTEXT_AMD64_FLOATING_POINT)),
464 # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
465 ("debug_control", EnableOnFlag(ctypes.c_uint64,
466 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
467 ("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64,
468 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
469 ("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64,
470 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
471 ("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64,
472 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
473 ("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64,
474 MD_CONTEXT_AMD64_DEBUG_REGISTERS))
475])
476
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100477MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
478 ("start", ctypes.c_uint64),
479 ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
480])
481
482MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
483 ("start", ctypes.c_uint64),
484 ("size", ctypes.c_uint64)
485])
486
487MINIDUMP_MEMORY_LIST = Descriptor([
488 ("range_count", ctypes.c_uint32),
489 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
490])
491
Ben Murdoch014dc512016-03-22 12:00:34 +0000492MINIDUMP_MEMORY_LIST_Mac = Descriptor([
493 ("range_count", ctypes.c_uint32),
494 ("junk", ctypes.c_uint32),
495 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
496])
497
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100498MINIDUMP_MEMORY_LIST64 = Descriptor([
499 ("range_count", ctypes.c_uint64),
500 ("base_rva", ctypes.c_uint64),
501 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count)
502])
503
504MINIDUMP_THREAD = Descriptor([
505 ("id", ctypes.c_uint32),
506 ("suspend_count", ctypes.c_uint32),
507 ("priority_class", ctypes.c_uint32),
508 ("priority", ctypes.c_uint32),
509 ("ted", ctypes.c_uint64),
510 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype),
511 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
512])
513
514MINIDUMP_THREAD_LIST = Descriptor([
515 ("thread_count", ctypes.c_uint32),
516 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
517])
518
Ben Murdoch014dc512016-03-22 12:00:34 +0000519MINIDUMP_THREAD_LIST_Mac = Descriptor([
520 ("thread_count", ctypes.c_uint32),
521 ("junk", ctypes.c_uint32),
522 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
523])
524
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000525MINIDUMP_VS_FIXEDFILEINFO = Descriptor([
526 ("dwSignature", ctypes.c_uint32),
527 ("dwStrucVersion", ctypes.c_uint32),
528 ("dwFileVersionMS", ctypes.c_uint32),
529 ("dwFileVersionLS", ctypes.c_uint32),
530 ("dwProductVersionMS", ctypes.c_uint32),
531 ("dwProductVersionLS", ctypes.c_uint32),
532 ("dwFileFlagsMask", ctypes.c_uint32),
533 ("dwFileFlags", ctypes.c_uint32),
534 ("dwFileOS", ctypes.c_uint32),
535 ("dwFileType", ctypes.c_uint32),
536 ("dwFileSubtype", ctypes.c_uint32),
537 ("dwFileDateMS", ctypes.c_uint32),
538 ("dwFileDateLS", ctypes.c_uint32)
539])
540
541MINIDUMP_RAW_MODULE = Descriptor([
542 ("base_of_image", ctypes.c_uint64),
543 ("size_of_image", ctypes.c_uint32),
544 ("checksum", ctypes.c_uint32),
545 ("time_date_stamp", ctypes.c_uint32),
546 ("module_name_rva", ctypes.c_uint32),
547 ("version_info", MINIDUMP_VS_FIXEDFILEINFO.ctype),
548 ("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
549 ("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
550 ("reserved0", ctypes.c_uint32 * 2),
551 ("reserved1", ctypes.c_uint32 * 2)
552])
553
554MINIDUMP_MODULE_LIST = Descriptor([
555 ("number_of_modules", ctypes.c_uint32),
556 ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules)
557])
558
Ben Murdoch014dc512016-03-22 12:00:34 +0000559MINIDUMP_MODULE_LIST_Mac = Descriptor([
560 ("number_of_modules", ctypes.c_uint32),
561 ("junk", ctypes.c_uint32),
562 ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules)
563])
564
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100565MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
566 ("processor_architecture", ctypes.c_uint16)
567])
568
569MD_CPU_ARCHITECTURE_X86 = 0
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000570MD_CPU_ARCHITECTURE_ARM = 5
Ben Murdoch014dc512016-03-22 12:00:34 +0000571MD_CPU_ARCHITECTURE_ARM64 = 0x8003
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100572MD_CPU_ARCHITECTURE_AMD64 = 9
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100573
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000574class FuncSymbol:
575 def __init__(self, start, size, name):
576 self.start = start
577 self.end = self.start + size
578 self.name = name
579
580 def __cmp__(self, other):
581 if isinstance(other, FuncSymbol):
582 return self.start - other.start
583 return self.start - other
584
585 def Covers(self, addr):
586 return (self.start <= addr) and (addr < self.end)
587
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100588class MinidumpReader(object):
589 """Minidump (.dmp) reader."""
590
591 _HEADER_MAGIC = 0x504d444d
592
593 def __init__(self, options, minidump_name):
594 self.minidump_name = minidump_name
595 self.minidump_file = open(minidump_name, "r")
596 self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE)
597 self.header = MINIDUMP_HEADER.Read(self.minidump, 0)
598 if self.header.signature != MinidumpReader._HEADER_MAGIC:
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000599 print >>sys.stderr, "Warning: Unsupported minidump header magic!"
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100600 DebugPrint(self.header)
601 directories = []
602 offset = self.header.stream_directories_rva
603 for _ in xrange(self.header.stream_count):
604 directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
605 offset += MINIDUMP_DIRECTORY.size
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100606 self.arch = None
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100607 self.exception = None
608 self.exception_context = None
609 self.memory_list = None
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000610 self.memory_list64 = None
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000611 self.module_list = None
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100612 self.thread_map = {}
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100613
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000614 self.symdir = options.symdir
615 self.modules_with_symbols = []
616 self.symbols = []
617
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100618 # Find MDRawSystemInfo stream and determine arch.
619 for d in directories:
620 if d.stream_type == MD_SYSTEM_INFO_STREAM:
621 system_info = MINIDUMP_RAW_SYSTEM_INFO.Read(
622 self.minidump, d.location.rva)
623 self.arch = system_info.processor_architecture
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000624 assert self.arch in [MD_CPU_ARCHITECTURE_AMD64,
625 MD_CPU_ARCHITECTURE_ARM,
Ben Murdoch014dc512016-03-22 12:00:34 +0000626 MD_CPU_ARCHITECTURE_ARM64,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000627 MD_CPU_ARCHITECTURE_X86]
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100628 assert not self.arch is None
629
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100630 for d in directories:
631 DebugPrint(d)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100632 if d.stream_type == MD_EXCEPTION_STREAM:
633 self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
634 self.minidump, d.location.rva)
635 DebugPrint(self.exception)
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100636 if self.arch == MD_CPU_ARCHITECTURE_X86:
637 self.exception_context = MINIDUMP_CONTEXT_X86.Read(
638 self.minidump, self.exception.thread_context.rva)
639 elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
640 self.exception_context = MINIDUMP_CONTEXT_AMD64.Read(
641 self.minidump, self.exception.thread_context.rva)
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000642 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
643 self.exception_context = MINIDUMP_CONTEXT_ARM.Read(
644 self.minidump, self.exception.thread_context.rva)
Ben Murdoch014dc512016-03-22 12:00:34 +0000645 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
646 self.exception_context = MINIDUMP_CONTEXT_ARM64.Read(
647 self.minidump, self.exception.thread_context.rva)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100648 DebugPrint(self.exception_context)
649 elif d.stream_type == MD_THREAD_LIST_STREAM:
650 thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
Ben Murdoch014dc512016-03-22 12:00:34 +0000651 if ctypes.sizeof(thread_list) + 4 == d.location.data_size:
652 thread_list = MINIDUMP_THREAD_LIST_Mac.Read(
653 self.minidump, d.location.rva)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100654 assert ctypes.sizeof(thread_list) == d.location.data_size
655 DebugPrint(thread_list)
656 for thread in thread_list.threads:
657 DebugPrint(thread)
658 self.thread_map[thread.id] = thread
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000659 elif d.stream_type == MD_MODULE_LIST_STREAM:
660 assert self.module_list is None
661 self.module_list = MINIDUMP_MODULE_LIST.Read(
662 self.minidump, d.location.rva)
Ben Murdoch014dc512016-03-22 12:00:34 +0000663 if ctypes.sizeof(self.module_list) + 4 == d.location.data_size:
664 self.module_list = MINIDUMP_MODULE_LIST_Mac.Read(
665 self.minidump, d.location.rva)
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000666 assert ctypes.sizeof(self.module_list) == d.location.data_size
Ben Murdoch014dc512016-03-22 12:00:34 +0000667 DebugPrint(self.module_list)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100668 elif d.stream_type == MD_MEMORY_LIST_STREAM:
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000669 print >>sys.stderr, "Warning: This is not a full minidump!"
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100670 assert self.memory_list is None
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000671 self.memory_list = MINIDUMP_MEMORY_LIST.Read(
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100672 self.minidump, d.location.rva)
Ben Murdoch014dc512016-03-22 12:00:34 +0000673 if ctypes.sizeof(self.memory_list) + 4 == d.location.data_size:
674 self.memory_list = MINIDUMP_MEMORY_LIST_Mac.Read(
675 self.minidump, d.location.rva)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100676 assert ctypes.sizeof(self.memory_list) == d.location.data_size
677 DebugPrint(self.memory_list)
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000678 elif d.stream_type == MD_MEMORY_64_LIST_STREAM:
679 assert self.memory_list64 is None
680 self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read(
681 self.minidump, d.location.rva)
682 assert ctypes.sizeof(self.memory_list64) == d.location.data_size
683 DebugPrint(self.memory_list64)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100684
685 def IsValidAddress(self, address):
686 return self.FindLocation(address) is not None
687
688 def ReadU8(self, address):
689 location = self.FindLocation(address)
690 return ctypes.c_uint8.from_buffer(self.minidump, location).value
691
692 def ReadU32(self, address):
693 location = self.FindLocation(address)
694 return ctypes.c_uint32.from_buffer(self.minidump, location).value
695
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100696 def ReadU64(self, address):
697 location = self.FindLocation(address)
698 return ctypes.c_uint64.from_buffer(self.minidump, location).value
699
700 def ReadUIntPtr(self, address):
701 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
702 return self.ReadU64(address)
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000703 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
704 return self.ReadU32(address)
Ben Murdoch014dc512016-03-22 12:00:34 +0000705 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
706 return self.ReadU64(address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100707 elif self.arch == MD_CPU_ARCHITECTURE_X86:
708 return self.ReadU32(address)
709
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100710 def ReadBytes(self, address, size):
711 location = self.FindLocation(address)
712 return self.minidump[location:location + size]
713
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000714 def _ReadWord(self, location):
715 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
716 return ctypes.c_uint64.from_buffer(self.minidump, location).value
717 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
718 return ctypes.c_uint32.from_buffer(self.minidump, location).value
Ben Murdoch014dc512016-03-22 12:00:34 +0000719 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
720 return ctypes.c_uint64.from_buffer(self.minidump, location).value
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000721 elif self.arch == MD_CPU_ARCHITECTURE_X86:
722 return ctypes.c_uint32.from_buffer(self.minidump, location).value
723
724 def IsProbableASCIIRegion(self, location, length):
725 ascii_bytes = 0
726 non_ascii_bytes = 0
Ben Murdoch014dc512016-03-22 12:00:34 +0000727 for i in xrange(length):
728 loc = location + i
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000729 byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
730 if byte >= 0x7f:
731 non_ascii_bytes += 1
732 if byte < 0x20 and byte != 0:
733 non_ascii_bytes += 1
734 if byte < 0x7f and byte >= 0x20:
735 ascii_bytes += 1
736 if byte == 0xa: # newline
737 ascii_bytes += 1
738 if ascii_bytes * 10 <= length:
739 return False
740 if length > 0 and ascii_bytes > non_ascii_bytes * 7:
741 return True
742 if ascii_bytes > non_ascii_bytes * 3:
743 return None # Maybe
744 return False
745
746 def IsProbableExecutableRegion(self, location, length):
747 opcode_bytes = 0
748 sixty_four = self.arch == MD_CPU_ARCHITECTURE_AMD64
Ben Murdoch014dc512016-03-22 12:00:34 +0000749 for i in xrange(length):
750 loc = location + i
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000751 byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
752 if (byte == 0x8b or # mov
753 byte == 0x89 or # mov reg-reg
754 (byte & 0xf0) == 0x50 or # push/pop
755 (sixty_four and (byte & 0xf0) == 0x40) or # rex prefix
756 byte == 0xc3 or # return
757 byte == 0x74 or # jeq
758 byte == 0x84 or # jeq far
759 byte == 0x75 or # jne
760 byte == 0x85 or # jne far
761 byte == 0xe8 or # call
762 byte == 0xe9 or # jmp far
763 byte == 0xeb): # jmp near
764 opcode_bytes += 1
765 opcode_percent = (opcode_bytes * 100) / length
766 threshold = 20
767 if opcode_percent > threshold + 2:
768 return True
769 if opcode_percent > threshold - 2:
770 return None # Maybe
771 return False
772
773 def FindRegion(self, addr):
774 answer = [-1, -1]
775 def is_in(reader, start, size, location):
776 if addr >= start and addr < start + size:
777 answer[0] = start
778 answer[1] = size
779 self.ForEachMemoryRegion(is_in)
780 if answer[0] == -1:
781 return None
782 return answer
783
784 def ForEachMemoryRegion(self, cb):
785 if self.memory_list64 is not None:
786 for r in self.memory_list64.ranges:
787 location = self.memory_list64.base_rva + offset
788 cb(self, r.start, r.size, location)
789 offset += r.size
790
791 if self.memory_list is not None:
792 for r in self.memory_list.ranges:
793 cb(self, r.start, r.memory.data_size, r.memory.rva)
794
795 def FindWord(self, word, alignment=0):
796 def search_inside_region(reader, start, size, location):
797 location = (location + alignment) & ~alignment
Ben Murdoch014dc512016-03-22 12:00:34 +0000798 for i in xrange(size - self.PointerSize()):
799 loc = location + i
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000800 if reader._ReadWord(loc) == word:
801 slot = start + (loc - location)
802 print "%s: %s" % (reader.FormatIntPtr(slot),
803 reader.FormatIntPtr(word))
804 self.ForEachMemoryRegion(search_inside_region)
805
806 def FindWordList(self, word):
807 aligned_res = []
808 unaligned_res = []
809 def search_inside_region(reader, start, size, location):
Ben Murdoch014dc512016-03-22 12:00:34 +0000810 for i in xrange(size - self.PointerSize()):
811 loc = location + i
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000812 if reader._ReadWord(loc) == word:
813 slot = start + (loc - location)
814 if slot % self.PointerSize() == 0:
815 aligned_res.append(slot)
816 else:
817 unaligned_res.append(slot)
818 self.ForEachMemoryRegion(search_inside_region)
819 return (aligned_res, unaligned_res)
820
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100821 def FindLocation(self, address):
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100822 offset = 0
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000823 if self.memory_list64 is not None:
824 for r in self.memory_list64.ranges:
825 if r.start <= address < r.start + r.size:
826 return self.memory_list64.base_rva + offset + address - r.start
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000827 offset += r.size
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000828 if self.memory_list is not None:
829 for r in self.memory_list.ranges:
830 if r.start <= address < r.start + r.memory.data_size:
831 return r.memory.rva + address - r.start
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100832 return None
833
834 def GetDisasmLines(self, address, size):
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000835 def CountUndefinedInstructions(lines):
836 pattern = "<UNDEFINED>"
837 return sum([line.count(pattern) for (ignore, line) in lines])
838
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100839 location = self.FindLocation(address)
840 if location is None: return []
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100841 arch = None
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000842 possible_objdump_flags = [""]
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100843 if self.arch == MD_CPU_ARCHITECTURE_X86:
844 arch = "ia32"
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000845 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
846 arch = "arm"
847 possible_objdump_flags = ["", "--disassembler-options=force-thumb"]
Ben Murdoch014dc512016-03-22 12:00:34 +0000848 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
849 arch = "arm64"
850 possible_objdump_flags = ["", "--disassembler-options=force-thumb"]
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100851 elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
852 arch = "x64"
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000853 results = [ disasm.GetDisasmLines(self.minidump_name,
854 location,
855 size,
856 arch,
857 False,
858 objdump_flags)
859 for objdump_flags in possible_objdump_flags ]
860 return min(results, key=CountUndefinedInstructions)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100861
862
863 def Dispose(self):
864 self.minidump.close()
865 self.minidump_file.close()
866
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100867 def ExceptionIP(self):
868 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
869 return self.exception_context.rip
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000870 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
871 return self.exception_context.pc
Ben Murdoch014dc512016-03-22 12:00:34 +0000872 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
873 return self.exception_context.pc
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100874 elif self.arch == MD_CPU_ARCHITECTURE_X86:
875 return self.exception_context.eip
876
877 def ExceptionSP(self):
878 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
879 return self.exception_context.rsp
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000880 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
881 return self.exception_context.sp
Ben Murdoch014dc512016-03-22 12:00:34 +0000882 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
883 return self.exception_context.sp
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100884 elif self.arch == MD_CPU_ARCHITECTURE_X86:
885 return self.exception_context.esp
886
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000887 def ExceptionFP(self):
888 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
889 return self.exception_context.rbp
890 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
891 return None
Ben Murdoch014dc512016-03-22 12:00:34 +0000892 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
893 return self.exception_context.fp
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000894 elif self.arch == MD_CPU_ARCHITECTURE_X86:
895 return self.exception_context.ebp
896
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100897 def FormatIntPtr(self, value):
898 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
899 return "%016x" % value
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000900 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
901 return "%08x" % value
Ben Murdoch014dc512016-03-22 12:00:34 +0000902 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
903 return "%016x" % value
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100904 elif self.arch == MD_CPU_ARCHITECTURE_X86:
905 return "%08x" % value
906
907 def PointerSize(self):
908 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
909 return 8
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000910 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
911 return 4
Ben Murdoch014dc512016-03-22 12:00:34 +0000912 elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
913 return 8
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100914 elif self.arch == MD_CPU_ARCHITECTURE_X86:
915 return 4
916
917 def Register(self, name):
918 return self.exception_context.__getattribute__(name)
919
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000920 def ReadMinidumpString(self, rva):
921 string = bytearray(MINIDUMP_STRING.Read(self.minidump, rva).buffer)
922 string = string.decode("utf16")
923 return string[0:len(string) - 1]
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100924
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000925 # Load FUNC records from a BreakPad symbol file
926 #
927 # http://code.google.com/p/google-breakpad/wiki/SymbolFiles
928 #
929 def _LoadSymbolsFrom(self, symfile, baseaddr):
930 print "Loading symbols from %s" % (symfile)
931 funcs = []
932 with open(symfile) as f:
933 for line in f:
934 result = re.match(
935 r"^FUNC ([a-f0-9]+) ([a-f0-9]+) ([a-f0-9]+) (.*)$", line)
936 if result is not None:
937 start = int(result.group(1), 16)
938 size = int(result.group(2), 16)
939 name = result.group(4).rstrip()
940 bisect.insort_left(self.symbols,
941 FuncSymbol(baseaddr + start, size, name))
942 print " ... done"
943
944 def TryLoadSymbolsFor(self, modulename, module):
945 try:
946 symfile = os.path.join(self.symdir,
947 modulename.replace('.', '_') + ".pdb.sym")
948 if os.path.isfile(symfile):
949 self._LoadSymbolsFrom(symfile, module.base_of_image)
950 self.modules_with_symbols.append(module)
951 except Exception as e:
952 print " ... failure (%s)" % (e)
953
954 # Returns true if address is covered by some module that has loaded symbols.
955 def _IsInModuleWithSymbols(self, addr):
956 for module in self.modules_with_symbols:
957 start = module.base_of_image
958 end = start + module.size_of_image
959 if (start <= addr) and (addr < end):
960 return True
961 return False
962
963 # Find symbol covering the given address and return its name in format
964 # <symbol name>+<offset from the start>
965 def FindSymbol(self, addr):
966 if not self._IsInModuleWithSymbols(addr):
967 return None
968
969 i = bisect.bisect_left(self.symbols, addr)
970 symbol = None
971 if (0 < i) and self.symbols[i - 1].Covers(addr):
972 symbol = self.symbols[i - 1]
973 elif (i < len(self.symbols)) and self.symbols[i].Covers(addr):
974 symbol = self.symbols[i]
975 else:
976 return None
977 diff = addr - symbol.start
978 return "%s+0x%x" % (symbol.name, diff)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100979
980
981class Printer(object):
982 """Printer with indentation support."""
983
984 def __init__(self):
985 self.indent = 0
986
987 def Indent(self):
988 self.indent += 2
989
990 def Dedent(self):
991 self.indent -= 2
992
993 def Print(self, string):
994 print "%s%s" % (self._IndentString(), string)
995
996 def PrintLines(self, lines):
997 indent = self._IndentString()
998 print "\n".join("%s%s" % (indent, line) for line in lines)
999
1000 def _IndentString(self):
1001 return self.indent * " "
1002
1003
1004ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
1005
1006
1007def FormatDisasmLine(start, heap, line):
1008 line_address = start + line[0]
1009 stack_slot = heap.stack_map.get(line_address)
1010 marker = " "
1011 if stack_slot:
1012 marker = "=>"
1013 code = AnnotateAddresses(heap, line[1])
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001014
1015 # Compute the actual call target which the disassembler is too stupid
1016 # to figure out (it adds the call offset to the disassembly offset rather
1017 # than the absolute instruction address).
1018 if heap.reader.arch == MD_CPU_ARCHITECTURE_X86:
1019 if code.startswith("e8"):
1020 words = code.split()
1021 if len(words) > 6 and words[5] == "call":
1022 offset = int(words[4] + words[3] + words[2] + words[1], 16)
1023 target = (line_address + offset + 5) & 0xFFFFFFFF
1024 code = code.replace(words[6], "0x%08x" % target)
1025 # TODO(jkummerow): port this hack to ARM and x64.
1026
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001027 return "%s%08x %08x: %s" % (marker, line_address, line[0], code)
1028
1029
1030def AnnotateAddresses(heap, line):
1031 extra = []
1032 for m in ADDRESS_RE.finditer(line):
1033 maybe_address = int(m.group(0), 16)
1034 object = heap.FindObject(maybe_address)
1035 if not object: continue
1036 extra.append(str(object))
1037 if len(extra) == 0: return line
1038 return "%s ;; %s" % (line, ", ".join(extra))
1039
1040
1041class HeapObject(object):
1042 def __init__(self, heap, map, address):
1043 self.heap = heap
1044 self.map = map
1045 self.address = address
1046
1047 def Is(self, cls):
1048 return isinstance(self, cls)
1049
1050 def Print(self, p):
1051 p.Print(str(self))
1052
1053 def __str__(self):
Ben Murdoch014dc512016-03-22 12:00:34 +00001054 instance_type = "???"
1055 if self.map is not None:
1056 instance_type = INSTANCE_TYPES[self.map.instance_type]
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001057 return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address),
Ben Murdoch014dc512016-03-22 12:00:34 +00001058 instance_type)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001059
1060 def ObjectField(self, offset):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001061 field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001062 return self.heap.FindObjectOrSmi(field_value)
1063
1064 def SmiField(self, offset):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001065 field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001066 if (field_value & 1) == 0:
1067 return field_value / 2
1068 return None
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001069
1070
1071class Map(HeapObject):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001072 def Decode(self, offset, size, value):
1073 return (value >> offset) & ((1 << size) - 1)
1074
1075 # Instance Sizes
1076 def InstanceSizesOffset(self):
1077 return self.heap.PointerSize()
1078
1079 def InstanceSizeOffset(self):
1080 return self.InstanceSizesOffset()
1081
1082 def InObjectProperties(self):
1083 return self.InstanceSizeOffset() + 1
1084
1085 def PreAllocatedPropertyFields(self):
1086 return self.InObjectProperties() + 1
1087
1088 def VisitorId(self):
1089 return self.PreAllocatedPropertyFields() + 1
1090
1091 # Instance Attributes
1092 def InstanceAttributesOffset(self):
1093 return self.InstanceSizesOffset() + self.heap.IntSize()
1094
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001095 def InstanceTypeOffset(self):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001096 return self.InstanceAttributesOffset()
1097
1098 def UnusedPropertyFieldsOffset(self):
1099 return self.InstanceTypeOffset() + 1
1100
1101 def BitFieldOffset(self):
1102 return self.UnusedPropertyFieldsOffset() + 1
1103
1104 def BitField2Offset(self):
1105 return self.BitFieldOffset() + 1
1106
1107 # Other fields
1108 def PrototypeOffset(self):
1109 return self.InstanceAttributesOffset() + self.heap.IntSize()
1110
1111 def ConstructorOffset(self):
1112 return self.PrototypeOffset() + self.heap.PointerSize()
1113
1114 def TransitionsOrBackPointerOffset(self):
1115 return self.ConstructorOffset() + self.heap.PointerSize()
1116
1117 def DescriptorsOffset(self):
1118 return self.TransitionsOrBackPointerOffset() + self.heap.PointerSize()
1119
1120 def CodeCacheOffset(self):
1121 return self.DescriptorsOffset() + self.heap.PointerSize()
1122
1123 def DependentCodeOffset(self):
1124 return self.CodeCacheOffset() + self.heap.PointerSize()
1125
1126 def BitField3Offset(self):
1127 return self.DependentCodeOffset() + self.heap.PointerSize()
1128
1129 def ReadByte(self, offset):
1130 return self.heap.reader.ReadU8(self.address + offset)
1131
1132 def Print(self, p):
1133 p.Print("Map(%08x)" % (self.address))
1134 p.Print("- size: %d, inobject: %d, preallocated: %d, visitor: %d" % (
1135 self.ReadByte(self.InstanceSizeOffset()),
1136 self.ReadByte(self.InObjectProperties()),
1137 self.ReadByte(self.PreAllocatedPropertyFields()),
1138 self.VisitorId()))
1139
1140 bitfield = self.ReadByte(self.BitFieldOffset())
1141 bitfield2 = self.ReadByte(self.BitField2Offset())
1142 p.Print("- %s, unused: %d, bf: %d, bf2: %d" % (
1143 INSTANCE_TYPES[self.ReadByte(self.InstanceTypeOffset())],
1144 self.ReadByte(self.UnusedPropertyFieldsOffset()),
1145 bitfield, bitfield2))
1146
1147 p.Print("- kind: %s" % (self.Decode(3, 5, bitfield2)))
1148
1149 bitfield3 = self.ObjectField(self.BitField3Offset())
1150 p.Print(
1151 "- EnumLength: %d NumberOfOwnDescriptors: %d OwnsDescriptors: %s" % (
1152 self.Decode(0, 11, bitfield3),
1153 self.Decode(11, 11, bitfield3),
1154 self.Decode(25, 1, bitfield3)))
1155 p.Print("- IsShared: %s" % (self.Decode(22, 1, bitfield3)))
1156 p.Print("- FunctionWithPrototype: %s" % (self.Decode(23, 1, bitfield3)))
1157 p.Print("- DictionaryMap: %s" % (self.Decode(24, 1, bitfield3)))
1158
1159 descriptors = self.ObjectField(self.DescriptorsOffset())
1160 if descriptors.__class__ == FixedArray:
1161 DescriptorArray(descriptors).Print(p)
1162 else:
1163 p.Print("Descriptors: %s" % (descriptors))
1164
1165 transitions = self.ObjectField(self.TransitionsOrBackPointerOffset())
1166 if transitions.__class__ == FixedArray:
1167 TransitionArray(transitions).Print(p)
1168 else:
1169 p.Print("TransitionsOrBackPointer: %s" % (transitions))
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001170
1171 def __init__(self, heap, map, address):
1172 HeapObject.__init__(self, heap, map, address)
1173 self.instance_type = \
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001174 heap.reader.ReadU8(self.address + self.InstanceTypeOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001175
1176
1177class String(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001178 def LengthOffset(self):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001179 # First word after the map is the hash, the second is the length.
1180 return self.heap.PointerSize() * 2
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001181
1182 def __init__(self, heap, map, address):
1183 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001184 self.length = self.SmiField(self.LengthOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001185
1186 def GetChars(self):
1187 return "?string?"
1188
1189 def Print(self, p):
1190 p.Print(str(self))
1191
1192 def __str__(self):
1193 return "\"%s\"" % self.GetChars()
1194
1195
1196class SeqString(String):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001197 def CharsOffset(self):
1198 return self.heap.PointerSize() * 3
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001199
1200 def __init__(self, heap, map, address):
1201 String.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001202 self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(),
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001203 self.length)
1204
1205 def GetChars(self):
1206 return self.chars
1207
1208
1209class ExternalString(String):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001210 # TODO(vegorov) fix ExternalString for X64 architecture
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001211 RESOURCE_OFFSET = 12
1212
1213 WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
1214 WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
1215
1216 def __init__(self, heap, map, address):
1217 String.__init__(self, heap, map, address)
1218 reader = heap.reader
1219 self.resource = \
1220 reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
1221 self.chars = "?external string?"
1222 if not reader.IsValidAddress(self.resource): return
1223 string_impl_address = self.resource + \
1224 ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
1225 if not reader.IsValidAddress(string_impl_address): return
1226 string_impl = reader.ReadU32(string_impl_address)
1227 chars_ptr_address = string_impl + \
1228 ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
1229 if not reader.IsValidAddress(chars_ptr_address): return
1230 chars_ptr = reader.ReadU32(chars_ptr_address)
1231 if not reader.IsValidAddress(chars_ptr): return
1232 raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
1233 self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
1234
1235 def GetChars(self):
1236 return self.chars
1237
1238
1239class ConsString(String):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001240 def LeftOffset(self):
1241 return self.heap.PointerSize() * 3
1242
1243 def RightOffset(self):
1244 return self.heap.PointerSize() * 4
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001245
1246 def __init__(self, heap, map, address):
1247 String.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001248 self.left = self.ObjectField(self.LeftOffset())
1249 self.right = self.ObjectField(self.RightOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001250
1251 def GetChars(self):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001252 try:
1253 return self.left.GetChars() + self.right.GetChars()
1254 except:
1255 return "***CAUGHT EXCEPTION IN GROKDUMP***"
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001256
1257
1258class Oddball(HeapObject):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001259 # Should match declarations in objects.h
1260 KINDS = [
1261 "False",
1262 "True",
1263 "TheHole",
1264 "Null",
1265 "ArgumentMarker",
1266 "Undefined",
1267 "Other"
1268 ]
1269
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001270 def ToStringOffset(self):
1271 return self.heap.PointerSize()
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001272
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001273 def ToNumberOffset(self):
1274 return self.ToStringOffset() + self.heap.PointerSize()
1275
1276 def KindOffset(self):
1277 return self.ToNumberOffset() + self.heap.PointerSize()
1278
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001279 def __init__(self, heap, map, address):
1280 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001281 self.to_string = self.ObjectField(self.ToStringOffset())
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001282 self.kind = self.SmiField(self.KindOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001283
1284 def Print(self, p):
1285 p.Print(str(self))
1286
1287 def __str__(self):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001288 if self.to_string:
1289 return "Oddball(%08x, <%s>)" % (self.address, str(self.to_string))
1290 else:
1291 kind = "???"
1292 if 0 <= self.kind < len(Oddball.KINDS):
1293 kind = Oddball.KINDS[self.kind]
1294 return "Oddball(%08x, kind=%s)" % (self.address, kind)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001295
1296
1297class FixedArray(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001298 def LengthOffset(self):
1299 return self.heap.PointerSize()
1300
1301 def ElementsOffset(self):
1302 return self.heap.PointerSize() * 2
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001303
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001304 def MemberOffset(self, i):
1305 return self.ElementsOffset() + self.heap.PointerSize() * i
1306
1307 def Get(self, i):
1308 return self.ObjectField(self.MemberOffset(i))
1309
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001310 def __init__(self, heap, map, address):
1311 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001312 self.length = self.SmiField(self.LengthOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001313
1314 def Print(self, p):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001315 p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address))
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001316 p.Indent()
1317 p.Print("length: %d" % self.length)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001318 base_offset = self.ElementsOffset()
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001319 for i in xrange(self.length):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001320 offset = base_offset + 4 * i
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001321 try:
1322 p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
1323 except TypeError:
1324 p.Dedent()
1325 p.Print("...")
1326 p.Print("}")
1327 return
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001328 p.Dedent()
1329 p.Print("}")
1330
1331 def __str__(self):
1332 return "FixedArray(%08x, length=%d)" % (self.address, self.length)
1333
1334
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001335class DescriptorArray(object):
1336 def __init__(self, array):
1337 self.array = array
1338
1339 def Length(self):
1340 return self.array.Get(0)
1341
1342 def Decode(self, offset, size, value):
1343 return (value >> offset) & ((1 << size) - 1)
1344
1345 TYPES = [
1346 "normal",
1347 "field",
1348 "function",
1349 "callbacks"
1350 ]
1351
1352 def Type(self, value):
1353 return DescriptorArray.TYPES[self.Decode(0, 3, value)]
1354
1355 def Attributes(self, value):
1356 attributes = self.Decode(3, 3, value)
1357 result = []
1358 if (attributes & 0): result += ["ReadOnly"]
1359 if (attributes & 1): result += ["DontEnum"]
1360 if (attributes & 2): result += ["DontDelete"]
1361 return "[" + (",".join(result)) + "]"
1362
1363 def Deleted(self, value):
1364 return self.Decode(6, 1, value) == 1
1365
1366 def FieldIndex(self, value):
1367 return self.Decode(20, 11, value)
1368
1369 def Pointer(self, value):
1370 return self.Decode(6, 11, value)
1371
1372 def Details(self, di, value):
1373 return (
1374 di,
1375 self.Type(value),
1376 self.Attributes(value),
1377 self.FieldIndex(value),
1378 self.Pointer(value)
1379 )
1380
1381
1382 def Print(self, p):
1383 length = self.Length()
1384 array = self.array
1385
1386 p.Print("Descriptors(%08x, length=%d)" % (array.address, length))
1387 p.Print("[et] %s" % (array.Get(1)))
1388
1389 for di in xrange(length):
1390 i = 2 + di * 3
1391 p.Print("0x%x" % (array.address + array.MemberOffset(i)))
1392 p.Print("[%i] name: %s" % (di, array.Get(i + 0)))
1393 p.Print("[%i] details: %s %s field-index %i pointer %i" % \
1394 self.Details(di, array.Get(i + 1)))
1395 p.Print("[%i] value: %s" % (di, array.Get(i + 2)))
1396
1397 end = self.array.length // 3
1398 if length != end:
1399 p.Print("[%i-%i] slack descriptors" % (length, end))
1400
1401
1402class TransitionArray(object):
1403 def __init__(self, array):
1404 self.array = array
1405
1406 def IsSimpleTransition(self):
1407 return self.array.length <= 2
1408
1409 def Length(self):
1410 # SimpleTransition cases
1411 if self.IsSimpleTransition():
1412 return self.array.length - 1
1413 return (self.array.length - 3) // 2
1414
1415 def Print(self, p):
1416 length = self.Length()
1417 array = self.array
1418
1419 p.Print("Transitions(%08x, length=%d)" % (array.address, length))
1420 p.Print("[backpointer] %s" % (array.Get(0)))
1421 if self.IsSimpleTransition():
1422 if length == 1:
1423 p.Print("[simple target] %s" % (array.Get(1)))
1424 return
1425
1426 elements = array.Get(1)
1427 if elements is not None:
1428 p.Print("[elements ] %s" % (elements))
1429
1430 prototype = array.Get(2)
1431 if prototype is not None:
1432 p.Print("[prototype ] %s" % (prototype))
1433
1434 for di in xrange(length):
1435 i = 3 + di * 2
1436 p.Print("[%i] symbol: %s" % (di, array.Get(i + 0)))
1437 p.Print("[%i] target: %s" % (di, array.Get(i + 1)))
1438
1439
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001440class JSFunction(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001441 def CodeEntryOffset(self):
1442 return 3 * self.heap.PointerSize()
1443
1444 def SharedOffset(self):
1445 return 5 * self.heap.PointerSize()
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001446
1447 def __init__(self, heap, map, address):
1448 HeapObject.__init__(self, heap, map, address)
1449 code_entry = \
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001450 heap.reader.ReadU32(self.address + self.CodeEntryOffset())
1451 self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1)
1452 self.shared = self.ObjectField(self.SharedOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001453
1454 def Print(self, p):
1455 source = "\n".join(" %s" % line for line in self._GetSource().split("\n"))
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001456 p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address))
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001457 p.Indent()
1458 p.Print("inferred name: %s" % self.shared.inferred_name)
1459 if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
1460 p.Print("script name: %s" % self.shared.script.name)
1461 p.Print("source:")
1462 p.PrintLines(self._GetSource().split("\n"))
1463 p.Print("code:")
1464 self.code.Print(p)
1465 if self.code != self.shared.code:
1466 p.Print("unoptimized code:")
1467 self.shared.code.Print(p)
1468 p.Dedent()
1469 p.Print("}")
1470
1471 def __str__(self):
1472 inferred_name = ""
Ben Murdoch014dc512016-03-22 12:00:34 +00001473 if self.shared is not None and self.shared.Is(SharedFunctionInfo):
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001474 inferred_name = self.shared.inferred_name
Ben Murdoch014dc512016-03-22 12:00:34 +00001475 return "JSFunction(%s, %s) " % \
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001476 (self.heap.reader.FormatIntPtr(self.address), inferred_name)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001477
1478 def _GetSource(self):
1479 source = "?source?"
1480 start = self.shared.start_position
1481 end = self.shared.end_position
1482 if not self.shared.script.Is(Script): return source
1483 script_source = self.shared.script.source
1484 if not script_source.Is(String): return source
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001485 if start and end:
1486 source = script_source.GetChars()[start:end]
1487 return source
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001488
1489
1490class SharedFunctionInfo(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001491 def CodeOffset(self):
1492 return 2 * self.heap.PointerSize()
1493
1494 def ScriptOffset(self):
1495 return 7 * self.heap.PointerSize()
1496
1497 def InferredNameOffset(self):
1498 return 9 * self.heap.PointerSize()
1499
1500 def EndPositionOffset(self):
1501 return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
1502
1503 def StartPositionAndTypeOffset(self):
1504 return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001505
1506 def __init__(self, heap, map, address):
1507 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001508 self.code = self.ObjectField(self.CodeOffset())
1509 self.script = self.ObjectField(self.ScriptOffset())
1510 self.inferred_name = self.ObjectField(self.InferredNameOffset())
1511 if heap.PointerSize() == 8:
1512 start_position_and_type = \
1513 heap.reader.ReadU32(self.StartPositionAndTypeOffset())
1514 self.start_position = start_position_and_type >> 2
1515 pseudo_smi_end_position = \
1516 heap.reader.ReadU32(self.EndPositionOffset())
1517 self.end_position = pseudo_smi_end_position >> 2
1518 else:
1519 start_position_and_type = \
1520 self.SmiField(self.StartPositionAndTypeOffset())
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001521 if start_position_and_type:
1522 self.start_position = start_position_and_type >> 2
1523 else:
1524 self.start_position = None
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001525 self.end_position = \
1526 self.SmiField(self.EndPositionOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001527
1528
1529class Script(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001530 def SourceOffset(self):
1531 return self.heap.PointerSize()
1532
1533 def NameOffset(self):
1534 return self.SourceOffset() + self.heap.PointerSize()
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001535
1536 def __init__(self, heap, map, address):
1537 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001538 self.source = self.ObjectField(self.SourceOffset())
1539 self.name = self.ObjectField(self.NameOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001540
1541
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001542class CodeCache(HeapObject):
1543 def DefaultCacheOffset(self):
1544 return self.heap.PointerSize()
1545
1546 def NormalTypeCacheOffset(self):
1547 return self.DefaultCacheOffset() + self.heap.PointerSize()
1548
1549 def __init__(self, heap, map, address):
1550 HeapObject.__init__(self, heap, map, address)
1551 self.default_cache = self.ObjectField(self.DefaultCacheOffset())
1552 self.normal_type_cache = self.ObjectField(self.NormalTypeCacheOffset())
1553
1554 def Print(self, p):
1555 p.Print("CodeCache(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1556 p.Indent()
1557 p.Print("default cache: %s" % self.default_cache)
1558 p.Print("normal type cache: %s" % self.normal_type_cache)
1559 p.Dedent()
1560 p.Print("}")
1561
1562
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001563class Code(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001564 CODE_ALIGNMENT_MASK = (1 << 5) - 1
1565
1566 def InstructionSizeOffset(self):
1567 return self.heap.PointerSize()
1568
1569 @staticmethod
1570 def HeaderSize(heap):
1571 return (heap.PointerSize() + heap.IntSize() + \
1572 4 * heap.PointerSize() + 3 * heap.IntSize() + \
1573 Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001574
1575 def __init__(self, heap, map, address):
1576 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001577 self.entry = self.address + Code.HeaderSize(heap)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001578 self.instruction_size = \
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001579 heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001580
1581 def Print(self, p):
1582 lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001583 p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address))
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001584 p.Indent()
1585 p.Print("instruction_size: %d" % self.instruction_size)
1586 p.PrintLines(self._FormatLine(line) for line in lines)
1587 p.Dedent()
1588 p.Print("}")
1589
1590 def _FormatLine(self, line):
1591 return FormatDisasmLine(self.entry, self.heap, line)
1592
1593
1594class V8Heap(object):
1595 CLASS_MAP = {
1596 "SYMBOL_TYPE": SeqString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001597 "ONE_BYTE_SYMBOL_TYPE": SeqString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001598 "CONS_SYMBOL_TYPE": ConsString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001599 "CONS_ONE_BYTE_SYMBOL_TYPE": ConsString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001600 "EXTERNAL_SYMBOL_TYPE": ExternalString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001601 "EXTERNAL_SYMBOL_WITH_ONE_BYTE_DATA_TYPE": ExternalString,
1602 "EXTERNAL_ONE_BYTE_SYMBOL_TYPE": ExternalString,
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001603 "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001604 "SHORT_EXTERNAL_SYMBOL_WITH_ONE_BYTE_DATA_TYPE": ExternalString,
1605 "SHORT_EXTERNAL_ONE_BYTE_SYMBOL_TYPE": ExternalString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001606 "STRING_TYPE": SeqString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001607 "ONE_BYTE_STRING_TYPE": SeqString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001608 "CONS_STRING_TYPE": ConsString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001609 "CONS_ONE_BYTE_STRING_TYPE": ConsString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001610 "EXTERNAL_STRING_TYPE": ExternalString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001611 "EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE": ExternalString,
1612 "EXTERNAL_ONE_BYTE_STRING_TYPE": ExternalString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001613 "MAP_TYPE": Map,
1614 "ODDBALL_TYPE": Oddball,
1615 "FIXED_ARRAY_TYPE": FixedArray,
1616 "JS_FUNCTION_TYPE": JSFunction,
1617 "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
1618 "SCRIPT_TYPE": Script,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001619 "CODE_CACHE_TYPE": CodeCache,
1620 "CODE_TYPE": Code,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001621 }
1622
1623 def __init__(self, reader, stack_map):
1624 self.reader = reader
1625 self.stack_map = stack_map
1626 self.objects = {}
1627
1628 def FindObjectOrSmi(self, tagged_address):
1629 if (tagged_address & 1) == 0: return tagged_address / 2
1630 return self.FindObject(tagged_address)
1631
1632 def FindObject(self, tagged_address):
1633 if tagged_address in self.objects:
1634 return self.objects[tagged_address]
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001635 if (tagged_address & self.ObjectAlignmentMask()) != 1: return None
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001636 address = tagged_address - 1
1637 if not self.reader.IsValidAddress(address): return None
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001638 map_tagged_address = self.reader.ReadUIntPtr(address)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001639 if tagged_address == map_tagged_address:
1640 # Meta map?
1641 meta_map = Map(self, None, address)
1642 instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
1643 if instance_type_name != "MAP_TYPE": return None
1644 meta_map.map = meta_map
1645 object = meta_map
1646 else:
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001647 map = self.FindMap(map_tagged_address)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001648 if map is None: return None
1649 instance_type_name = INSTANCE_TYPES.get(map.instance_type)
1650 if instance_type_name is None: return None
1651 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
1652 object = cls(self, map, address)
1653 self.objects[tagged_address] = object
1654 return object
1655
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001656 def FindMap(self, tagged_address):
1657 if (tagged_address & self.MapAlignmentMask()) != 1: return None
1658 address = tagged_address - 1
1659 if not self.reader.IsValidAddress(address): return None
1660 object = Map(self, None, address)
1661 return object
1662
1663 def IntSize(self):
1664 return 4
1665
1666 def PointerSize(self):
1667 return self.reader.PointerSize()
1668
1669 def ObjectAlignmentMask(self):
1670 return self.PointerSize() - 1
1671
1672 def MapAlignmentMask(self):
1673 if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64:
1674 return (1 << 4) - 1
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001675 elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM:
1676 return (1 << 4) - 1
Ben Murdoch014dc512016-03-22 12:00:34 +00001677 elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM64:
1678 return (1 << 4) - 1
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001679 elif self.reader.arch == MD_CPU_ARCHITECTURE_X86:
1680 return (1 << 5) - 1
1681
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001682 def PageAlignmentMask(self):
1683 return (1 << 20) - 1
1684
1685
1686class KnownObject(HeapObject):
1687 def __init__(self, heap, known_name):
1688 HeapObject.__init__(self, heap, None, None)
1689 self.known_name = known_name
1690
1691 def __str__(self):
1692 return "<%s>" % self.known_name
1693
1694
1695class KnownMap(HeapObject):
1696 def __init__(self, heap, known_name, instance_type):
1697 HeapObject.__init__(self, heap, None, None)
1698 self.instance_type = instance_type
1699 self.known_name = known_name
1700
1701 def __str__(self):
1702 return "<%s>" % self.known_name
1703
1704
1705COMMENT_RE = re.compile(r"^C (0x[0-9a-fA-F]+) (.*)$")
1706PAGEADDRESS_RE = re.compile(
Ben Murdoch014dc512016-03-22 12:00:34 +00001707 r"^P (mappage|oldpage) (0x[0-9a-fA-F]+)$")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001708
1709
1710class InspectionInfo(object):
1711 def __init__(self, minidump_name, reader):
1712 self.comment_file = minidump_name + ".comments"
1713 self.address_comments = {}
1714 self.page_address = {}
1715 if os.path.exists(self.comment_file):
1716 with open(self.comment_file, "r") as f:
1717 lines = f.readlines()
1718 f.close()
1719
1720 for l in lines:
1721 m = COMMENT_RE.match(l)
1722 if m:
1723 self.address_comments[int(m.group(1), 0)] = m.group(2)
1724 m = PAGEADDRESS_RE.match(l)
1725 if m:
1726 self.page_address[m.group(1)] = int(m.group(2), 0)
1727 self.reader = reader
1728 self.styles = {}
1729 self.color_addresses()
1730 return
1731
1732 def get_page_address(self, page_kind):
1733 return self.page_address.get(page_kind, 0)
1734
1735 def save_page_address(self, page_kind, address):
1736 with open(self.comment_file, "a") as f:
1737 f.write("P %s 0x%x\n" % (page_kind, address))
1738 f.close()
1739
1740 def color_addresses(self):
1741 # Color all stack addresses.
1742 exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
1743 stack_top = self.reader.ExceptionSP()
1744 stack_bottom = exception_thread.stack.start + \
1745 exception_thread.stack.memory.data_size
1746 frame_pointer = self.reader.ExceptionFP()
1747 self.styles[frame_pointer] = "frame"
1748 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
Ben Murdochf3b273f2017-01-17 12:11:28 +00001749 # stack address
1750 self.styles[slot] = "sa"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001751 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
1752 maybe_address = self.reader.ReadUIntPtr(slot)
Ben Murdochf3b273f2017-01-17 12:11:28 +00001753 # stack value
1754 self.styles[maybe_address] = "sv"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001755 if slot == frame_pointer:
1756 self.styles[slot] = "frame"
1757 frame_pointer = maybe_address
1758 self.styles[self.reader.ExceptionIP()] = "pc"
1759
1760 def get_style_class(self, address):
1761 return self.styles.get(address, None)
1762
1763 def get_style_class_string(self, address):
1764 style = self.get_style_class(address)
1765 if style != None:
Ben Murdochf3b273f2017-01-17 12:11:28 +00001766 return " class=%s " % style
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001767 else:
1768 return ""
1769
1770 def set_comment(self, address, comment):
1771 self.address_comments[address] = comment
1772 with open(self.comment_file, "a") as f:
1773 f.write("C 0x%x %s\n" % (address, comment))
1774 f.close()
1775
1776 def get_comment(self, address):
1777 return self.address_comments.get(address, "")
1778
1779
1780class InspectionPadawan(object):
1781 """The padawan can improve annotations by sensing well-known objects."""
1782 def __init__(self, reader, heap):
1783 self.reader = reader
1784 self.heap = heap
1785 self.known_first_map_page = 0
Ben Murdoch014dc512016-03-22 12:00:34 +00001786 self.known_first_old_page = 0
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001787
1788 def __getattr__(self, name):
1789 """An InspectionPadawan can be used instead of V8Heap, even though
1790 it does not inherit from V8Heap (aka. mixin)."""
1791 return getattr(self.heap, name)
1792
1793 def GetPageOffset(self, tagged_address):
1794 return tagged_address & self.heap.PageAlignmentMask()
1795
1796 def IsInKnownMapSpace(self, tagged_address):
1797 page_address = tagged_address & ~self.heap.PageAlignmentMask()
1798 return page_address == self.known_first_map_page
1799
1800 def IsInKnownOldSpace(self, tagged_address):
1801 page_address = tagged_address & ~self.heap.PageAlignmentMask()
Ben Murdoch014dc512016-03-22 12:00:34 +00001802 return page_address == self.known_first_old_page
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001803
1804 def ContainingKnownOldSpaceName(self, tagged_address):
1805 page_address = tagged_address & ~self.heap.PageAlignmentMask()
Ben Murdoch014dc512016-03-22 12:00:34 +00001806 if page_address == self.known_first_old_page: return "OLD_SPACE"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001807 return None
1808
1809 def SenseObject(self, tagged_address):
1810 if self.IsInKnownOldSpace(tagged_address):
1811 offset = self.GetPageOffset(tagged_address)
1812 lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset)
1813 known_obj_name = KNOWN_OBJECTS.get(lookup_key)
1814 if known_obj_name:
1815 return KnownObject(self, known_obj_name)
1816 if self.IsInKnownMapSpace(tagged_address):
1817 known_map = self.SenseMap(tagged_address)
1818 if known_map:
1819 return known_map
1820 found_obj = self.heap.FindObject(tagged_address)
1821 if found_obj: return found_obj
1822 address = tagged_address - 1
1823 if self.reader.IsValidAddress(address):
1824 map_tagged_address = self.reader.ReadUIntPtr(address)
1825 map = self.SenseMap(map_tagged_address)
1826 if map is None: return None
1827 instance_type_name = INSTANCE_TYPES.get(map.instance_type)
1828 if instance_type_name is None: return None
1829 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
1830 return cls(self, map, address)
1831 return None
1832
1833 def SenseMap(self, tagged_address):
1834 if self.IsInKnownMapSpace(tagged_address):
1835 offset = self.GetPageOffset(tagged_address)
1836 known_map_info = KNOWN_MAPS.get(offset)
1837 if known_map_info:
1838 known_map_type, known_map_name = known_map_info
1839 return KnownMap(self, known_map_name, known_map_type)
1840 found_map = self.heap.FindMap(tagged_address)
1841 if found_map: return found_map
1842 return None
1843
1844 def FindObjectOrSmi(self, tagged_address):
1845 """When used as a mixin in place of V8Heap."""
1846 found_obj = self.SenseObject(tagged_address)
1847 if found_obj: return found_obj
1848 if (tagged_address & 1) == 0:
1849 return "Smi(%d)" % (tagged_address / 2)
1850 else:
1851 return "Unknown(%s)" % self.reader.FormatIntPtr(tagged_address)
1852
1853 def FindObject(self, tagged_address):
1854 """When used as a mixin in place of V8Heap."""
1855 raise NotImplementedError
1856
1857 def FindMap(self, tagged_address):
1858 """When used as a mixin in place of V8Heap."""
1859 raise NotImplementedError
1860
1861 def PrintKnowledge(self):
1862 print " known_first_map_page = %s\n"\
Ben Murdoch014dc512016-03-22 12:00:34 +00001863 " known_first_old_page = %s" % (
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001864 self.reader.FormatIntPtr(self.known_first_map_page),
Ben Murdoch014dc512016-03-22 12:00:34 +00001865 self.reader.FormatIntPtr(self.known_first_old_page))
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001866
1867WEB_HEADER = """
1868<!DOCTYPE html>
1869<html>
1870<head>
1871<meta content="text/html; charset=utf-8" http-equiv="content-type">
1872<style media="screen" type="text/css">
1873
1874.code {
1875 font-family: monospace;
1876}
1877
1878.dmptable {
1879 border-collapse : collapse;
1880 border-spacing : 0px;
Ben Murdochf3b273f2017-01-17 12:11:28 +00001881 table-layout: fixed;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001882}
1883
1884.codedump {
1885 border-collapse : collapse;
1886 border-spacing : 0px;
Ben Murdochf3b273f2017-01-17 12:11:28 +00001887 table-layout: fixed;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001888}
1889
1890.addrcomments {
1891 border : 0px;
1892}
1893
1894.register {
1895 padding-right : 1em;
1896}
1897
1898.header {
1899 clear : both;
1900}
1901
1902.header .navigation {
1903 float : left;
1904}
1905
1906.header .dumpname {
1907 float : right;
1908}
1909
1910tr.highlight-line {
1911 background-color : yellow;
1912}
1913
1914.highlight {
1915 background-color : magenta;
1916}
1917
1918tr.inexact-highlight-line {
1919 background-color : pink;
1920}
1921
1922input {
1923 background-color: inherit;
1924 border: 1px solid LightGray;
1925}
1926
1927.dumpcomments {
1928 border : 1px solid LightGray;
1929 width : 32em;
1930}
1931
1932.regions td {
1933 padding:0 15px 0 15px;
1934}
1935
1936.stackframe td {
1937 background-color : cyan;
1938}
1939
Ben Murdochf3b273f2017-01-17 12:11:28 +00001940.stackaddress, .sa {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001941 background-color : LightGray;
1942}
1943
Ben Murdochf3b273f2017-01-17 12:11:28 +00001944.stackval, .sv {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001945 background-color : LightCyan;
1946}
1947
1948.frame {
1949 background-color : cyan;
1950}
1951
Ben Murdochf3b273f2017-01-17 12:11:28 +00001952.commentinput, .ci {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001953 width : 20em;
1954}
1955
Ben Murdochf3b273f2017-01-17 12:11:28 +00001956/* a.nodump */
1957a.nd:visited {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001958 color : black;
1959 text-decoration : none;
1960}
1961
Ben Murdochf3b273f2017-01-17 12:11:28 +00001962a.nd:link {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001963 color : black;
1964 text-decoration : none;
1965}
1966
1967a:visited {
1968 color : blueviolet;
1969}
1970
1971a:link {
1972 color : blue;
1973}
1974
1975.disasmcomment {
1976 color : DarkGreen;
1977}
1978
1979</style>
1980
1981<script type="application/javascript">
1982
1983var address_str = "address-";
1984var address_len = address_str.length;
1985
1986function comment() {
1987 var s = event.srcElement.id;
1988 var index = s.indexOf(address_str);
1989 if (index >= 0) {
1990 send_comment(s.substring(index + address_len), event.srcElement.value);
1991 }
1992}
Ben Murdochf3b273f2017-01-17 12:11:28 +00001993var c = comment;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001994
1995function send_comment(address, comment) {
1996 xmlhttp = new XMLHttpRequest();
1997 address = encodeURIComponent(address)
1998 comment = encodeURIComponent(comment)
1999 xmlhttp.open("GET",
2000 "setcomment?%(query_dump)s&address=" + address +
2001 "&comment=" + comment, true);
2002 xmlhttp.send();
2003}
2004
2005var dump_str = "dump-";
2006var dump_len = dump_str.length;
2007
2008function dump_comment() {
2009 var s = event.srcElement.id;
2010 var index = s.indexOf(dump_str);
2011 if (index >= 0) {
2012 send_dump_desc(s.substring(index + dump_len), event.srcElement.value);
2013 }
2014}
2015
2016function send_dump_desc(name, desc) {
2017 xmlhttp = new XMLHttpRequest();
2018 name = encodeURIComponent(name)
2019 desc = encodeURIComponent(desc)
2020 xmlhttp.open("GET",
2021 "setdumpdesc?dump=" + name +
2022 "&description=" + desc, true);
2023 xmlhttp.send();
2024}
2025
2026function onpage(kind, address) {
2027 xmlhttp = new XMLHttpRequest();
2028 kind = encodeURIComponent(kind)
2029 address = encodeURIComponent(address)
2030 xmlhttp.onreadystatechange = function() {
2031 if (xmlhttp.readyState==4 && xmlhttp.status==200) {
2032 location.reload(true)
2033 }
2034 };
2035 xmlhttp.open("GET",
2036 "setpageaddress?%(query_dump)s&kind=" + kind +
2037 "&address=" + address);
2038 xmlhttp.send();
2039}
2040
2041</script>
2042
2043<title>Dump %(dump_name)s</title>
2044</head>
2045
2046<body>
2047 <div class="header">
Ben Murdochf3b273f2017-01-17 12:11:28 +00002048 <form class="navigation" action=/search.html">
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002049 <a href="summary.html?%(query_dump)s">Context info</a>&nbsp;&nbsp;&nbsp;
2050 <a href="info.html?%(query_dump)s">Dump info</a>&nbsp;&nbsp;&nbsp;
2051 <a href="modules.html?%(query_dump)s">Modules</a>&nbsp;&nbsp;&nbsp;
2052 &nbsp;
2053 <input type="search" name="val">
2054 <input type="submit" name="search" value="Search">
2055 <input type="hidden" name="dump" value="%(dump_name)s">
2056 </form>
2057 <form class="navigation" action="disasm.html#highlight">
2058 &nbsp;
2059 &nbsp;
2060 &nbsp;
2061 <input type="search" name="val">
2062 <input type="submit" name="disasm" value="Disasm">
2063 &nbsp;
2064 &nbsp;
2065 &nbsp;
2066 <a href="dumps.html">Dumps...</a>
2067 </form>
2068 </div>
2069 <br>
2070 <hr>
2071"""
2072
2073
2074WEB_FOOTER = """
2075</body>
2076</html>
2077"""
2078
2079
2080class WebParameterError(Exception):
2081 def __init__(self, message):
2082 Exception.__init__(self, message)
2083
2084
2085class InspectionWebHandler(BaseHTTPServer.BaseHTTPRequestHandler):
2086 def formatter(self, query_components):
2087 name = query_components.get("dump", [None])[0]
2088 return self.server.get_dump_formatter(name)
2089
2090 def send_success_html_headers(self):
2091 self.send_response(200)
2092 self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
2093 self.send_header("Pragma", "no-cache")
2094 self.send_header("Expires", "0")
2095 self.send_header('Content-type','text/html')
2096 self.end_headers()
2097 return
2098
2099 def do_GET(self):
2100 try:
2101 parsedurl = urlparse.urlparse(self.path)
2102 query_components = urlparse.parse_qs(parsedurl.query)
2103 if parsedurl.path == "/dumps.html":
2104 self.send_success_html_headers()
Ben Murdochf3b273f2017-01-17 12:11:28 +00002105 out_buffer = StringIO.StringIO()
2106 self.server.output_dumps(out_buffer)
2107 self.wfile.write(out_buffer.getvalue())
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002108 elif parsedurl.path == "/summary.html":
2109 self.send_success_html_headers()
Ben Murdochf3b273f2017-01-17 12:11:28 +00002110 out_buffer = StringIO.StringIO()
2111 self.formatter(query_components).output_summary(out_buffer)
2112 self.wfile.write(out_buffer.getvalue())
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002113 elif parsedurl.path == "/info.html":
2114 self.send_success_html_headers()
Ben Murdochf3b273f2017-01-17 12:11:28 +00002115 out_buffer = StringIO.StringIO()
2116 self.formatter(query_components).output_info(out_buffer)
2117 self.wfile.write(out_buffer.getvalue())
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002118 elif parsedurl.path == "/modules.html":
2119 self.send_success_html_headers()
Ben Murdochf3b273f2017-01-17 12:11:28 +00002120 out_buffer = StringIO.StringIO()
2121 self.formatter(query_components).output_modules(out_buffer)
2122 self.wfile.write(out_buffer.getvalue())
2123 elif parsedurl.path == "/search.html" or parsedurl.path == "/s":
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002124 address = query_components.get("val", [])
2125 if len(address) != 1:
2126 self.send_error(404, "Invalid params")
2127 return
2128 self.send_success_html_headers()
Ben Murdochf3b273f2017-01-17 12:11:28 +00002129 out_buffer = StringIO.StringIO()
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002130 self.formatter(query_components).output_search_res(
Ben Murdochf3b273f2017-01-17 12:11:28 +00002131 out_buffer, address[0])
2132 self.wfile.write(out_buffer.getvalue())
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002133 elif parsedurl.path == "/disasm.html":
2134 address = query_components.get("val", [])
2135 exact = query_components.get("exact", ["on"])
2136 if len(address) != 1:
2137 self.send_error(404, "Invalid params")
2138 return
2139 self.send_success_html_headers()
Ben Murdochf3b273f2017-01-17 12:11:28 +00002140 out_buffer = StringIO.StringIO()
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002141 self.formatter(query_components).output_disasm(
Ben Murdochf3b273f2017-01-17 12:11:28 +00002142 out_buffer, address[0], exact[0])
2143 self.wfile.write(out_buffer.getvalue())
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002144 elif parsedurl.path == "/data.html":
2145 address = query_components.get("val", [])
2146 datakind = query_components.get("type", ["address"])
2147 if len(address) == 1 and len(datakind) == 1:
2148 self.send_success_html_headers()
Ben Murdochf3b273f2017-01-17 12:11:28 +00002149 out_buffer = StringIO.StringIO()
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002150 self.formatter(query_components).output_data(
Ben Murdochf3b273f2017-01-17 12:11:28 +00002151 out_buffer, address[0], datakind[0])
2152 self.wfile.write(out_buffer.getvalue())
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002153 else:
2154 self.send_error(404,'Invalid params')
2155 elif parsedurl.path == "/setdumpdesc":
2156 name = query_components.get("dump", [""])
2157 description = query_components.get("description", [""])
2158 if len(name) == 1 and len(description) == 1:
2159 name = name[0]
2160 description = description[0]
2161 if self.server.set_dump_desc(name, description):
2162 self.send_success_html_headers()
2163 self.wfile.write("OK")
2164 return
2165 self.send_error(404,'Invalid params')
2166 elif parsedurl.path == "/setcomment":
2167 address = query_components.get("address", [])
2168 comment = query_components.get("comment", [""])
2169 if len(address) == 1 and len(comment) == 1:
2170 address = address[0]
2171 comment = comment[0]
2172 self.formatter(query_components).set_comment(address, comment)
2173 self.send_success_html_headers()
2174 self.wfile.write("OK")
2175 else:
2176 self.send_error(404,'Invalid params')
2177 elif parsedurl.path == "/setpageaddress":
2178 kind = query_components.get("kind", [])
2179 address = query_components.get("address", [""])
2180 if len(kind) == 1 and len(address) == 1:
2181 kind = kind[0]
2182 address = address[0]
2183 self.formatter(query_components).set_page_address(kind, address)
2184 self.send_success_html_headers()
2185 self.wfile.write("OK")
2186 else:
2187 self.send_error(404,'Invalid params')
2188 else:
2189 self.send_error(404,'File Not Found: %s' % self.path)
2190
2191 except IOError:
2192 self.send_error(404,'File Not Found: %s' % self.path)
2193
2194 except WebParameterError as e:
2195 self.send_error(404, 'Web parameter error: %s' % e.message)
2196
2197
Ben Murdoch014dc512016-03-22 12:00:34 +00002198HTML_REG_FORMAT = "<span class=\"register\"><b>%s</b>:&nbsp;%s</span><br/>\n"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002199
2200
2201class InspectionWebFormatter(object):
2202 CONTEXT_FULL = 0
2203 CONTEXT_SHORT = 1
2204
2205 def __init__(self, switches, minidump_name, http_server):
2206 self.dumpfilename = os.path.split(minidump_name)[1]
2207 self.encfilename = urllib.urlencode({ 'dump' : self.dumpfilename })
2208 self.reader = MinidumpReader(switches, minidump_name)
2209 self.server = http_server
2210
2211 # Set up the heap
2212 exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
2213 stack_top = self.reader.ExceptionSP()
2214 stack_bottom = exception_thread.stack.start + \
2215 exception_thread.stack.memory.data_size
2216 stack_map = {self.reader.ExceptionIP(): -1}
2217 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
2218 maybe_address = self.reader.ReadUIntPtr(slot)
2219 if not maybe_address in stack_map:
2220 stack_map[maybe_address] = slot
2221 self.heap = V8Heap(self.reader, stack_map)
2222
2223 self.padawan = InspectionPadawan(self.reader, self.heap)
2224 self.comments = InspectionInfo(minidump_name, self.reader)
Ben Murdoch014dc512016-03-22 12:00:34 +00002225 self.padawan.known_first_old_page = (
2226 self.comments.get_page_address("oldpage"))
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002227 self.padawan.known_first_map_page = (
2228 self.comments.get_page_address("mappage"))
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002229
2230 def set_comment(self, straddress, comment):
2231 try:
2232 address = int(straddress, 0)
2233 self.comments.set_comment(address, comment)
2234 except ValueError:
2235 print "Invalid address"
2236
2237 def set_page_address(self, kind, straddress):
2238 try:
2239 address = int(straddress, 0)
Ben Murdoch014dc512016-03-22 12:00:34 +00002240 if kind == "oldpage":
2241 self.padawan.known_first_old_page = address
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002242 elif kind == "mappage":
2243 self.padawan.known_first_map_page = address
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002244 self.comments.save_page_address(kind, address)
2245 except ValueError:
2246 print "Invalid address"
2247
2248 def td_from_address(self, f, address):
2249 f.write("<td %s>" % self.comments.get_style_class_string(address))
2250
2251 def format_address(self, maybeaddress, straddress = None):
2252 if maybeaddress is None:
2253 return "not in dump"
2254 else:
2255 if straddress is None:
2256 straddress = "0x" + self.reader.FormatIntPtr(maybeaddress)
2257 style_class = ""
2258 if not self.reader.IsValidAddress(maybeaddress):
Ben Murdochf3b273f2017-01-17 12:11:28 +00002259 style_class = "class=nd"
2260 return ("<a %s href=s?%s&amp;val=%s>%s</a>" %
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002261 (style_class, self.encfilename, straddress, straddress))
2262
2263 def output_header(self, f):
2264 f.write(WEB_HEADER %
2265 { "query_dump" : self.encfilename,
2266 "dump_name" : cgi.escape(self.dumpfilename) })
2267
2268 def output_footer(self, f):
2269 f.write(WEB_FOOTER)
2270
Ben Murdochf3b273f2017-01-17 12:11:28 +00002271 MAX_CONTEXT_STACK = 2048
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002272
2273 def output_summary(self, f):
2274 self.output_header(f)
2275 f.write('<div class="code">')
2276 self.output_context(f, InspectionWebFormatter.CONTEXT_SHORT)
2277 self.output_disasm_pc(f)
2278
2279 # Output stack
2280 exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002281 stack_top = self.reader.ExceptionSP()
Ben Murdochf3b273f2017-01-17 12:11:28 +00002282 stack_bottom = min(exception_thread.stack.start + \
2283 exception_thread.stack.memory.data_size,
2284 stack_top + self.MAX_CONTEXT_STACK)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002285 self.output_words(f, stack_top - 16, stack_bottom, stack_top, "Stack")
2286
2287 f.write('</div>')
2288 self.output_footer(f)
2289 return
2290
2291 def output_info(self, f):
2292 self.output_header(f)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002293 f.write("<h3>Dump info</h3>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002294 f.write("Description: ")
2295 self.server.output_dump_desc_field(f, self.dumpfilename)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002296 f.write("<br>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002297 f.write("Filename: ")
Ben Murdochf3b273f2017-01-17 12:11:28 +00002298 f.write("<span class=\"code\">%s</span><br>" % (self.dumpfilename))
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002299 dt = datetime.datetime.fromtimestamp(self.reader.header.time_date_stampt)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002300 f.write("Timestamp: %s<br>" % dt.strftime('%Y-%m-%d %H:%M:%S'))
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002301 self.output_context(f, InspectionWebFormatter.CONTEXT_FULL)
2302 self.output_address_ranges(f)
2303 self.output_footer(f)
2304 return
2305
2306 def output_address_ranges(self, f):
2307 regions = {}
2308 def print_region(_reader, start, size, _location):
2309 regions[start] = size
2310 self.reader.ForEachMemoryRegion(print_region)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002311 f.write("<h3>Available memory regions</h3>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002312 f.write('<div class="code">')
Ben Murdochf3b273f2017-01-17 12:11:28 +00002313 f.write("<table class=\"regions\">")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002314 f.write("<thead><tr>")
2315 f.write("<th>Start address</th>")
2316 f.write("<th>End address</th>")
2317 f.write("<th>Number of bytes</th>")
Ben Murdochf3b273f2017-01-17 12:11:28 +00002318 f.write("</tr></thead>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002319 for start in sorted(regions):
2320 size = regions[start]
2321 f.write("<tr>")
2322 f.write("<td>%s</td>" % self.format_address(start))
2323 f.write("<td>&nbsp;%s</td>" % self.format_address(start + size))
2324 f.write("<td>&nbsp;%d</td>" % size)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002325 f.write("</tr>")
2326 f.write("</table>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002327 f.write('</div>')
2328 return
2329
2330 def output_module_details(self, f, module):
2331 f.write("<b>%s</b>" % GetModuleName(self.reader, module))
2332 file_version = GetVersionString(module.version_info.dwFileVersionMS,
2333 module.version_info.dwFileVersionLS)
2334 product_version = GetVersionString(module.version_info.dwProductVersionMS,
2335 module.version_info.dwProductVersionLS)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002336 f.write("<br>&nbsp;&nbsp;")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002337 f.write("base: %s" % self.reader.FormatIntPtr(module.base_of_image))
Ben Murdochf3b273f2017-01-17 12:11:28 +00002338 f.write("<br>&nbsp;&nbsp;")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002339 f.write(" end: %s" % self.reader.FormatIntPtr(module.base_of_image +
2340 module.size_of_image))
Ben Murdochf3b273f2017-01-17 12:11:28 +00002341 f.write("<br>&nbsp;&nbsp;")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002342 f.write(" file version: %s" % file_version)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002343 f.write("<br>&nbsp;&nbsp;")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002344 f.write(" product version: %s" % product_version)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002345 f.write("<br>&nbsp;&nbsp;")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002346 time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
2347 f.write(" timestamp: %s" % time_date_stamp)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002348 f.write("<br>");
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002349
2350 def output_modules(self, f):
2351 self.output_header(f)
2352 f.write('<div class="code">')
2353 for module in self.reader.module_list.modules:
2354 self.output_module_details(f, module)
2355 f.write("</div>")
2356 self.output_footer(f)
2357 return
2358
2359 def output_context(self, f, details):
2360 exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
2361 f.write("<h3>Exception context</h3>")
Ben Murdochf3b273f2017-01-17 12:11:28 +00002362 f.write('<div class="code">')
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002363 f.write("Thread id: %d" % exception_thread.id)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002364 f.write("&nbsp;&nbsp; Exception code: %08X<br/>" %
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002365 self.reader.exception.exception.code)
2366 if details == InspectionWebFormatter.CONTEXT_FULL:
2367 if self.reader.exception.exception.parameter_count > 0:
Ben Murdochf3b273f2017-01-17 12:11:28 +00002368 f.write("&nbsp;&nbsp; Exception parameters: ")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002369 for i in xrange(0, self.reader.exception.exception.parameter_count):
2370 f.write("%08x" % self.reader.exception.exception.information[i])
Ben Murdochf3b273f2017-01-17 12:11:28 +00002371 f.write("<br><br>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002372
2373 for r in CONTEXT_FOR_ARCH[self.reader.arch]:
2374 f.write(HTML_REG_FORMAT %
2375 (r, self.format_address(self.reader.Register(r))))
2376 # TODO(vitalyr): decode eflags.
Ben Murdoch014dc512016-03-22 12:00:34 +00002377 if self.reader.arch in [MD_CPU_ARCHITECTURE_ARM, MD_CPU_ARCHITECTURE_ARM64]:
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002378 f.write("<b>cpsr</b>: %s" % bin(self.reader.exception_context.cpsr)[2:])
2379 else:
2380 f.write("<b>eflags</b>: %s" %
2381 bin(self.reader.exception_context.eflags)[2:])
Ben Murdochf3b273f2017-01-17 12:11:28 +00002382 f.write('</div>')
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002383 return
2384
2385 def align_down(self, a, size):
2386 alignment_correction = a % size
2387 return a - alignment_correction
2388
2389 def align_up(self, a, size):
2390 alignment_correction = (size - 1) - ((a + size - 1) % size)
2391 return a + alignment_correction
2392
2393 def format_object(self, address):
2394 heap_object = self.padawan.SenseObject(address)
2395 return cgi.escape(str(heap_object or ""))
2396
2397 def output_data(self, f, straddress, datakind):
2398 try:
2399 self.output_header(f)
2400 address = int(straddress, 0)
2401 if not self.reader.IsValidAddress(address):
2402 f.write("<h3>Address 0x%x not found in the dump.</h3>" % address)
2403 return
2404 region = self.reader.FindRegion(address)
2405 if datakind == "address":
2406 self.output_words(f, region[0], region[0] + region[1], address, "Dump")
2407 elif datakind == "ascii":
2408 self.output_ascii(f, region[0], region[0] + region[1], address)
2409 self.output_footer(f)
2410
2411 except ValueError:
2412 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
2413 return
2414
2415 def output_words(self, f, start_address, end_address,
2416 highlight_address, desc):
2417 region = self.reader.FindRegion(highlight_address)
2418 if region is None:
Ben Murdochf3b273f2017-01-17 12:11:28 +00002419 f.write("<h3>Address 0x%x not found in the dump.</h3>" %
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002420 (highlight_address))
2421 return
2422 size = self.heap.PointerSize()
2423 start_address = self.align_down(start_address, size)
2424 low = self.align_down(region[0], size)
2425 high = self.align_up(region[0] + region[1], size)
2426 if start_address < low:
2427 start_address = low
2428 end_address = self.align_up(end_address, size)
2429 if end_address > high:
2430 end_address = high
2431
2432 expand = ""
2433 if start_address != low or end_address != high:
2434 expand = ("(<a href=\"data.html?%s&amp;val=0x%x#highlight\">"
2435 " more..."
2436 " </a>)" %
2437 (self.encfilename, highlight_address))
2438
2439 f.write("<h3>%s 0x%x - 0x%x, "
Ben Murdochf3b273f2017-01-17 12:11:28 +00002440 "highlighting <a href=\"#highlight\">0x%x</a> %s</h3>" %
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002441 (desc, start_address, end_address, highlight_address, expand))
2442 f.write('<div class="code">')
Ben Murdochf3b273f2017-01-17 12:11:28 +00002443 f.write("<table class=codedump>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002444
Ben Murdoch014dc512016-03-22 12:00:34 +00002445 for j in xrange(0, end_address - start_address, size):
2446 slot = start_address + j
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002447 heap_object = ""
2448 maybe_address = None
2449 end_region = region[0] + region[1]
2450 if slot < region[0] or slot + size > end_region:
2451 straddress = "0x"
2452 for i in xrange(end_region, slot + size):
2453 straddress += "??"
2454 for i in reversed(
2455 xrange(max(slot, region[0]), min(slot + size, end_region))):
2456 straddress += "%02x" % self.reader.ReadU8(i)
2457 for i in xrange(slot, region[0]):
2458 straddress += "??"
2459 else:
2460 maybe_address = self.reader.ReadUIntPtr(slot)
2461 straddress = self.format_address(maybe_address)
2462 if maybe_address:
2463 heap_object = self.format_object(maybe_address)
2464
Ben Murdochf3b273f2017-01-17 12:11:28 +00002465 address_fmt = "%s&nbsp;</td>"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002466 if slot == highlight_address:
Ben Murdochf3b273f2017-01-17 12:11:28 +00002467 f.write("<tr class=highlight-line>")
2468 address_fmt = "<a id=highlight></a>%s&nbsp;</td>"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002469 elif slot < highlight_address and highlight_address < slot + size:
Ben Murdochf3b273f2017-01-17 12:11:28 +00002470 f.write("<tr class=inexact-highlight-line>")
2471 address_fmt = "<a id=highlight></a>%s&nbsp;</td>"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002472 else:
Ben Murdochf3b273f2017-01-17 12:11:28 +00002473 f.write("<tr>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002474
Ben Murdochf3b273f2017-01-17 12:11:28 +00002475 f.write("<td>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002476 self.output_comment_box(f, "da-", slot)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002477 f.write("</td>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002478 self.td_from_address(f, slot)
2479 f.write(address_fmt % self.format_address(slot))
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002480 self.td_from_address(f, maybe_address)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002481 f.write(":&nbsp;%s&nbsp;</td>" % straddress)
2482 f.write("<td>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002483 if maybe_address != None:
2484 self.output_comment_box(
2485 f, "sv-" + self.reader.FormatIntPtr(slot), maybe_address)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002486 f.write("</td>")
2487 f.write("<td>%s</td>" % (heap_object or ''))
2488 f.write("</tr>")
2489 f.write("</table>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002490 f.write("</div>")
2491 return
2492
2493 def output_ascii(self, f, start_address, end_address, highlight_address):
2494 region = self.reader.FindRegion(highlight_address)
2495 if region is None:
2496 f.write("<h3>Address %x not found in the dump.</h3>" %
2497 highlight_address)
2498 return
2499 if start_address < region[0]:
2500 start_address = region[0]
2501 if end_address > region[0] + region[1]:
2502 end_address = region[0] + region[1]
2503
2504 expand = ""
2505 if start_address != region[0] or end_address != region[0] + region[1]:
2506 link = ("data.html?%s&amp;val=0x%x&amp;type=ascii#highlight" %
2507 (self.encfilename, highlight_address))
2508 expand = "(<a href=\"%s\">more...</a>)" % link
2509
2510 f.write("<h3>ASCII dump 0x%x - 0x%x, highlighting 0x%x %s</h3>" %
2511 (start_address, end_address, highlight_address, expand))
2512
2513 line_width = 64
2514
2515 f.write('<div class="code">')
2516
2517 start = self.align_down(start_address, line_width)
2518
Ben Murdoch014dc512016-03-22 12:00:34 +00002519 for i in xrange(end_address - start):
2520 address = start + i
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002521 if address % 64 == 0:
2522 if address != start:
2523 f.write("<br>")
2524 f.write("0x%08x:&nbsp;" % address)
2525 if address < start_address:
2526 f.write("&nbsp;")
2527 else:
2528 if address == highlight_address:
2529 f.write("<span class=\"highlight\">")
2530 code = self.reader.ReadU8(address)
2531 if code < 127 and code >= 32:
2532 f.write("&#")
2533 f.write(str(code))
2534 f.write(";")
2535 else:
2536 f.write("&middot;")
2537 if address == highlight_address:
2538 f.write("</span>")
2539 f.write("</div>")
2540 return
2541
2542 def output_disasm(self, f, straddress, strexact):
2543 try:
2544 self.output_header(f)
2545 address = int(straddress, 0)
2546 if not self.reader.IsValidAddress(address):
2547 f.write("<h3>Address 0x%x not found in the dump.</h3>" % address)
2548 return
2549 region = self.reader.FindRegion(address)
2550 self.output_disasm_range(
2551 f, region[0], region[0] + region[1], address, strexact == "on")
2552 self.output_footer(f)
2553 except ValueError:
2554 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
2555 return
2556
2557 def output_disasm_range(
2558 self, f, start_address, end_address, highlight_address, exact):
2559 region = self.reader.FindRegion(highlight_address)
2560 if start_address < region[0]:
2561 start_address = region[0]
2562 if end_address > region[0] + region[1]:
2563 end_address = region[0] + region[1]
2564 count = end_address - start_address
2565 lines = self.reader.GetDisasmLines(start_address, count)
2566 found = False
2567 if exact:
2568 for line in lines:
2569 if line[0] + start_address == highlight_address:
2570 found = True
2571 break
2572 if not found:
2573 start_address = highlight_address
2574 count = end_address - start_address
2575 lines = self.reader.GetDisasmLines(highlight_address, count)
2576 expand = ""
2577 if start_address != region[0] or end_address != region[0] + region[1]:
2578 exactness = ""
2579 if exact and not found and end_address == region[0] + region[1]:
2580 exactness = "&amp;exact=off"
2581 expand = ("(<a href=\"disasm.html?%s%s"
2582 "&amp;val=0x%x#highlight\">more...</a>)" %
2583 (self.encfilename, exactness, highlight_address))
2584
2585 f.write("<h3>Disassembling 0x%x - 0x%x, highlighting 0x%x %s</h3>" %
2586 (start_address, end_address, highlight_address, expand))
2587 f.write('<div class="code">')
Ben Murdochf3b273f2017-01-17 12:11:28 +00002588 f.write("<table class=\"codedump\">");
Ben Murdoch014dc512016-03-22 12:00:34 +00002589 for i in xrange(len(lines)):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002590 line = lines[i]
2591 next_address = count
2592 if i + 1 < len(lines):
2593 next_line = lines[i + 1]
2594 next_address = next_line[0]
2595 self.format_disasm_line(
2596 f, start_address, line, next_address, highlight_address)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002597 f.write("</table>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002598 f.write("</div>")
2599 return
2600
2601 def annotate_disasm_addresses(self, line):
2602 extra = []
2603 for m in ADDRESS_RE.finditer(line):
2604 maybe_address = int(m.group(0), 16)
2605 formatted_address = self.format_address(maybe_address, m.group(0))
2606 line = line.replace(m.group(0), formatted_address)
2607 object_info = self.padawan.SenseObject(maybe_address)
2608 if not object_info:
2609 continue
2610 extra.append(cgi.escape(str(object_info)))
2611 if len(extra) == 0:
2612 return line
Ben Murdochf3b273f2017-01-17 12:11:28 +00002613 return ("%s <span class=disasmcomment>;; %s</span>" %
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002614 (line, ", ".join(extra)))
2615
2616 def format_disasm_line(
2617 self, f, start, line, next_address, highlight_address):
2618 line_address = start + line[0]
Ben Murdochf3b273f2017-01-17 12:11:28 +00002619 address_fmt = " <td>%s</td>"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002620 if line_address == highlight_address:
Ben Murdochf3b273f2017-01-17 12:11:28 +00002621 f.write("<tr class=highlight-line>")
2622 address_fmt = " <td><a id=highlight>%s</a></td>"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002623 elif (line_address < highlight_address and
2624 highlight_address < next_address + start):
Ben Murdochf3b273f2017-01-17 12:11:28 +00002625 f.write("<tr class=inexact-highlight-line>")
2626 address_fmt = " <td><a id=highlight>%s</a></td>"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002627 else:
Ben Murdochf3b273f2017-01-17 12:11:28 +00002628 f.write("<tr>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002629 num_bytes = next_address - line[0]
2630 stack_slot = self.heap.stack_map.get(line_address)
2631 marker = ""
2632 if stack_slot:
2633 marker = "=>"
2634 op_offset = 3 * num_bytes - 1
2635
2636 code = line[1]
2637 # Compute the actual call target which the disassembler is too stupid
2638 # to figure out (it adds the call offset to the disassembly offset rather
2639 # than the absolute instruction address).
2640 if self.heap.reader.arch == MD_CPU_ARCHITECTURE_X86:
2641 if code.startswith("e8"):
2642 words = code.split()
2643 if len(words) > 6 and words[5] == "call":
2644 offset = int(words[4] + words[3] + words[2] + words[1], 16)
2645 target = (line_address + offset + 5) & 0xFFFFFFFF
2646 code = code.replace(words[6], "0x%08x" % target)
2647 # TODO(jkummerow): port this hack to ARM and x64.
2648
2649 opcodes = code[:op_offset]
2650 code = self.annotate_disasm_addresses(code[op_offset:])
2651 f.write(" <td>")
2652 self.output_comment_box(f, "codel-", line_address)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002653 f.write("</td>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002654 f.write(address_fmt % marker)
2655 f.write(" ")
2656 self.td_from_address(f, line_address)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002657 f.write(self.format_address(line_address))
2658 f.write(" (+0x%x)</td>" % line[0])
2659 f.write("<td>:&nbsp;%s&nbsp;</td>" % opcodes)
2660 f.write("<td>%s</td>" % code)
2661 f.write("</tr>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002662
2663 def output_comment_box(self, f, prefix, address):
Ben Murdochf3b273f2017-01-17 12:11:28 +00002664 comment = self.comments.get_comment(address)
2665 value = ""
2666 if comment:
2667 value = " value=\"%s\"" % cgi.escape(comment)
2668 f.write("<input type=text class=ci "
2669 "id=%s-address-0x%s onchange=c()%s>" %
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002670 (prefix,
2671 self.reader.FormatIntPtr(address),
Ben Murdochf3b273f2017-01-17 12:11:28 +00002672 value))
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002673
2674 MAX_FOUND_RESULTS = 100
2675
2676 def output_find_results(self, f, results):
2677 f.write("Addresses")
2678 toomany = len(results) > self.MAX_FOUND_RESULTS
2679 if toomany:
2680 f.write("(found %i results, displaying only first %i)" %
2681 (len(results), self.MAX_FOUND_RESULTS))
Ben Murdochf3b273f2017-01-17 12:11:28 +00002682 f.write(": ")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002683 results = sorted(results)
2684 results = results[:min(len(results), self.MAX_FOUND_RESULTS)]
2685 for address in results:
Ben Murdochf3b273f2017-01-17 12:11:28 +00002686 f.write("<span %s>%s</span>" %
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002687 (self.comments.get_style_class_string(address),
2688 self.format_address(address)))
2689 if toomany:
Ben Murdochf3b273f2017-01-17 12:11:28 +00002690 f.write("...")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002691
2692
2693 def output_page_info(self, f, page_kind, page_address, my_page_address):
2694 if my_page_address == page_address and page_address != 0:
Ben Murdochf3b273f2017-01-17 12:11:28 +00002695 f.write("Marked first %s page." % page_kind)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002696 else:
2697 f.write("<span id=\"%spage\" style=\"display:none\">" % page_kind)
2698 f.write("Marked first %s page." % page_kind)
2699 f.write("</span>\n")
2700 f.write("<button onclick=\"onpage('%spage', '0x%x')\">" %
2701 (page_kind, my_page_address))
Ben Murdochf3b273f2017-01-17 12:11:28 +00002702 f.write("Mark as first %s page</button>" % page_kind)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002703 return
2704
2705 def output_search_res(self, f, straddress):
2706 try:
2707 self.output_header(f)
2708 f.write("<h3>Search results for %s</h3>" % straddress)
2709
2710 address = int(straddress, 0)
2711
2712 f.write("Comment: ")
2713 self.output_comment_box(f, "search-", address)
Ben Murdochf3b273f2017-01-17 12:11:28 +00002714 f.write("<br>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002715
2716 page_address = address & ~self.heap.PageAlignmentMask()
2717
Ben Murdochf3b273f2017-01-17 12:11:28 +00002718 f.write("Page info: ")
Ben Murdoch014dc512016-03-22 12:00:34 +00002719 self.output_page_info(f, "old", self.padawan.known_first_old_page, \
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002720 page_address)
2721 self.output_page_info(f, "map", self.padawan.known_first_map_page, \
2722 page_address)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002723
2724 if not self.reader.IsValidAddress(address):
2725 f.write("<h3>The contents at address %s not found in the dump.</h3>" % \
2726 straddress)
2727 else:
2728 # Print as words
2729 self.output_words(f, address - 8, address + 32, address, "Dump")
2730
2731 # Print as ASCII
Ben Murdochf3b273f2017-01-17 12:11:28 +00002732 f.write("<hr>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002733 self.output_ascii(f, address, address + 256, address)
2734
2735 # Print as code
Ben Murdochf3b273f2017-01-17 12:11:28 +00002736 f.write("<hr>")
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002737 self.output_disasm_range(f, address - 16, address + 16, address, True)
2738
2739 aligned_res, unaligned_res = self.reader.FindWordList(address)
2740
2741 if len(aligned_res) > 0:
Ben Murdochf3b273f2017-01-17 12:11:28 +00002742 f.write("<h3>Occurrences of 0x%x at aligned addresses</h3>" %
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002743 address)
2744 self.output_find_results(f, aligned_res)
2745
2746 if len(unaligned_res) > 0:
Ben Murdochf3b273f2017-01-17 12:11:28 +00002747 f.write("<h3>Occurrences of 0x%x at unaligned addresses</h3>" % \
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002748 address)
2749 self.output_find_results(f, unaligned_res)
2750
2751 if len(aligned_res) + len(unaligned_res) == 0:
Ben Murdochf3b273f2017-01-17 12:11:28 +00002752 f.write("<h3>No occurences of 0x%x found in the dump</h3>" % address)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002753
2754 self.output_footer(f)
2755
2756 except ValueError:
2757 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
2758 return
2759
2760 def output_disasm_pc(self, f):
2761 address = self.reader.ExceptionIP()
2762 if not self.reader.IsValidAddress(address):
2763 return
2764 self.output_disasm_range(f, address - 16, address + 16, address, True)
2765
2766
2767WEB_DUMPS_HEADER = """
2768<!DOCTYPE html>
2769<html>
2770<head>
2771<meta content="text/html; charset=utf-8" http-equiv="content-type">
2772<style media="screen" type="text/css">
2773
2774.dumplist {
2775 border-collapse : collapse;
2776 border-spacing : 0px;
2777 font-family: monospace;
2778}
2779
2780.dumpcomments {
2781 border : 1px solid LightGray;
2782 width : 32em;
2783}
2784
2785</style>
2786
2787<script type="application/javascript">
2788
2789var dump_str = "dump-";
2790var dump_len = dump_str.length;
2791
2792function dump_comment() {
2793 var s = event.srcElement.id;
2794 var index = s.indexOf(dump_str);
2795 if (index >= 0) {
2796 send_dump_desc(s.substring(index + dump_len), event.srcElement.value);
2797 }
2798}
2799
2800function send_dump_desc(name, desc) {
2801 xmlhttp = new XMLHttpRequest();
2802 name = encodeURIComponent(name)
2803 desc = encodeURIComponent(desc)
2804 xmlhttp.open("GET",
2805 "setdumpdesc?dump=" + name +
2806 "&description=" + desc, true);
2807 xmlhttp.send();
2808}
2809
2810</script>
2811
2812<title>Dump list</title>
2813</head>
2814
2815<body>
2816"""
2817
2818WEB_DUMPS_FOOTER = """
2819</body>
2820</html>
2821"""
2822
2823DUMP_FILE_RE = re.compile(r"[-_0-9a-zA-Z][-\._0-9a-zA-Z]*\.dmp$")
2824
2825
2826class InspectionWebServer(BaseHTTPServer.HTTPServer):
2827 def __init__(self, port_number, switches, minidump_name):
2828 BaseHTTPServer.HTTPServer.__init__(
2829 self, ('', port_number), InspectionWebHandler)
2830 splitpath = os.path.split(minidump_name)
2831 self.dumppath = splitpath[0]
2832 self.dumpfilename = splitpath[1]
2833 self.default_formatter = InspectionWebFormatter(
2834 switches, minidump_name, self)
2835 self.formatters = { self.dumpfilename : self.default_formatter }
2836 self.switches = switches
2837
2838 def output_dump_desc_field(self, f, name):
2839 try:
2840 descfile = open(os.path.join(self.dumppath, name + ".desc"), "r")
2841 desc = descfile.readline()
2842 descfile.close()
2843 except IOError:
2844 desc = ""
2845 f.write("<input type=\"text\" class=\"dumpcomments\" "
2846 "id=\"dump-%s\" onchange=\"dump_comment()\" value=\"%s\">\n" %
2847 (cgi.escape(name), desc))
2848
2849 def set_dump_desc(self, name, description):
2850 if not DUMP_FILE_RE.match(name):
2851 return False
2852 fname = os.path.join(self.dumppath, name)
2853 if not os.path.isfile(fname):
2854 return False
2855 fname = fname + ".desc"
2856 descfile = open(fname, "w")
2857 descfile.write(description)
2858 descfile.close()
2859 return True
2860
2861 def get_dump_formatter(self, name):
2862 if name is None:
2863 return self.default_formatter
2864 else:
2865 if not DUMP_FILE_RE.match(name):
2866 raise WebParameterError("Invalid name '%s'" % name)
2867 formatter = self.formatters.get(name, None)
2868 if formatter is None:
2869 try:
2870 formatter = InspectionWebFormatter(
2871 self.switches, os.path.join(self.dumppath, name), self)
2872 self.formatters[name] = formatter
2873 except IOError:
2874 raise WebParameterError("Could not open dump '%s'" % name)
2875 return formatter
2876
2877 def output_dumps(self, f):
2878 f.write(WEB_DUMPS_HEADER)
2879 f.write("<h3>List of available dumps</h3>")
2880 f.write("<table class=\"dumplist\">\n")
2881 f.write("<thead><tr>")
2882 f.write("<th>Name</th>")
2883 f.write("<th>File time</th>")
2884 f.write("<th>Comment</th>")
2885 f.write("</tr></thead>")
2886 dumps_by_time = {}
2887 for fname in os.listdir(self.dumppath):
2888 if DUMP_FILE_RE.match(fname):
2889 mtime = os.stat(os.path.join(self.dumppath, fname)).st_mtime
2890 fnames = dumps_by_time.get(mtime, [])
2891 fnames.append(fname)
2892 dumps_by_time[mtime] = fnames
2893
2894 for mtime in sorted(dumps_by_time, reverse=True):
2895 fnames = dumps_by_time[mtime]
2896 for fname in fnames:
2897 f.write("<tr>\n")
2898 f.write("<td><a href=\"summary.html?%s\">%s</a></td>\n" % (
2899 (urllib.urlencode({ 'dump' : fname }), fname)))
2900 f.write("<td>&nbsp;&nbsp;&nbsp;")
2901 f.write(datetime.datetime.fromtimestamp(mtime))
2902 f.write("</td>")
2903 f.write("<td>&nbsp;&nbsp;&nbsp;")
2904 self.output_dump_desc_field(f, fname)
2905 f.write("</td>")
2906 f.write("</tr>\n")
2907 f.write("</table>\n")
2908 f.write(WEB_DUMPS_FOOTER)
2909 return
2910
2911class InspectionShell(cmd.Cmd):
2912 def __init__(self, reader, heap):
2913 cmd.Cmd.__init__(self)
2914 self.reader = reader
2915 self.heap = heap
2916 self.padawan = InspectionPadawan(reader, heap)
2917 self.prompt = "(grok) "
2918
2919 def do_da(self, address):
2920 """
2921 Print ASCII string starting at specified address.
2922 """
2923 address = int(address, 16)
2924 string = ""
2925 while self.reader.IsValidAddress(address):
2926 code = self.reader.ReadU8(address)
2927 if code < 128:
2928 string += chr(code)
2929 else:
2930 break
2931 address += 1
2932 if string == "":
2933 print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address)
2934 else:
2935 print "%s\n" % string
2936
Ben Murdoch014dc512016-03-22 12:00:34 +00002937 def do_dd(self, args):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002938 """
Ben Murdoch014dc512016-03-22 12:00:34 +00002939 Interpret memory in the given region [address, address + num * word_size)
2940 (if available) as a sequence of words. Automatic alignment is not performed.
2941 If the num is not specified, a default value of 16 words is used.
2942 Synopsis: dd 0x<address> 0x<num>
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002943 """
Ben Murdoch014dc512016-03-22 12:00:34 +00002944 args = args.split(' ')
2945 start = int(args[0], 16)
2946 num = int(args[1], 16) if len(args) > 1 else 0x10
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002947 if (start & self.heap.ObjectAlignmentMask()) != 0:
2948 print "Warning: Dumping un-aligned memory, is this what you had in mind?"
Ben Murdoch014dc512016-03-22 12:00:34 +00002949 for i in xrange(0,
2950 self.reader.PointerSize() * num,
2951 self.reader.PointerSize()):
2952 slot = start + i
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002953 if not self.reader.IsValidAddress(slot):
2954 print "Address is not contained within the minidump!"
2955 return
2956 maybe_address = self.reader.ReadUIntPtr(slot)
2957 heap_object = self.padawan.SenseObject(maybe_address)
2958 print "%s: %s %s" % (self.reader.FormatIntPtr(slot),
2959 self.reader.FormatIntPtr(maybe_address),
2960 heap_object or '')
2961
2962 def do_do(self, address):
2963 """
2964 Interpret memory at the given address as a V8 object. Automatic
2965 alignment makes sure that you can pass tagged as well as un-tagged
2966 addresses.
2967 """
2968 address = int(address, 16)
2969 if (address & self.heap.ObjectAlignmentMask()) == 0:
2970 address = address + 1
2971 elif (address & self.heap.ObjectAlignmentMask()) != 1:
2972 print "Address doesn't look like a valid pointer!"
2973 return
2974 heap_object = self.padawan.SenseObject(address)
2975 if heap_object:
2976 heap_object.Print(Printer())
2977 else:
2978 print "Address cannot be interpreted as object!"
2979
2980 def do_do_desc(self, address):
2981 """
2982 Print a descriptor array in a readable format.
2983 """
2984 start = int(address, 16)
2985 if ((start & 1) == 1): start = start - 1
2986 DescriptorArray(FixedArray(self.heap, None, start)).Print(Printer())
2987
2988 def do_do_map(self, address):
2989 """
2990 Print a descriptor array in a readable format.
2991 """
2992 start = int(address, 16)
2993 if ((start & 1) == 1): start = start - 1
2994 Map(self.heap, None, start).Print(Printer())
2995
2996 def do_do_trans(self, address):
2997 """
2998 Print a transition array in a readable format.
2999 """
3000 start = int(address, 16)
3001 if ((start & 1) == 1): start = start - 1
3002 TransitionArray(FixedArray(self.heap, None, start)).Print(Printer())
3003
3004 def do_dp(self, address):
3005 """
3006 Interpret memory at the given address as being on a V8 heap page
3007 and print information about the page header (if available).
3008 """
3009 address = int(address, 16)
3010 page_address = address & ~self.heap.PageAlignmentMask()
3011 if self.reader.IsValidAddress(page_address):
3012 raise NotImplementedError
3013 else:
3014 print "Page header is not available!"
3015
3016 def do_k(self, arguments):
3017 """
3018 Teach V8 heap layout information to the inspector. This increases
3019 the amount of annotations the inspector can produce while dumping
3020 data. The first page of each heap space is of particular interest
3021 because it contains known objects that do not move.
3022 """
3023 self.padawan.PrintKnowledge()
3024
Ben Murdoch014dc512016-03-22 12:00:34 +00003025 def do_ko(self, address):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003026 """
3027 Teach V8 heap layout information to the inspector. Set the first
Ben Murdoch014dc512016-03-22 12:00:34 +00003028 old space page by passing any pointer into that page.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003029 """
3030 address = int(address, 16)
3031 page_address = address & ~self.heap.PageAlignmentMask()
Ben Murdoch014dc512016-03-22 12:00:34 +00003032 self.padawan.known_first_old_page = page_address
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003033
3034 def do_km(self, address):
3035 """
3036 Teach V8 heap layout information to the inspector. Set the first
3037 map-space page by passing any pointer into that page.
3038 """
3039 address = int(address, 16)
3040 page_address = address & ~self.heap.PageAlignmentMask()
3041 self.padawan.known_first_map_page = page_address
3042
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003043 def do_list(self, smth):
3044 """
3045 List all available memory regions.
3046 """
3047 def print_region(reader, start, size, location):
3048 print " %s - %s (%d bytes)" % (reader.FormatIntPtr(start),
3049 reader.FormatIntPtr(start + size),
3050 size)
3051 print "Available memory regions:"
3052 self.reader.ForEachMemoryRegion(print_region)
3053
3054 def do_lm(self, arg):
3055 """
3056 List details for all loaded modules in the minidump. An argument can
3057 be passed to limit the output to only those modules that contain the
3058 argument as a substring (case insensitive match).
3059 """
3060 for module in self.reader.module_list.modules:
3061 if arg:
3062 name = GetModuleName(self.reader, module).lower()
3063 if name.find(arg.lower()) >= 0:
3064 PrintModuleDetails(self.reader, module)
3065 else:
3066 PrintModuleDetails(self.reader, module)
3067 print
3068
3069 def do_s(self, word):
3070 """
3071 Search for a given word in available memory regions. The given word
3072 is expanded to full pointer size and searched at aligned as well as
3073 un-aligned memory locations. Use 'sa' to search aligned locations
3074 only.
3075 """
3076 try:
3077 word = int(word, 0)
3078 except ValueError:
3079 print "Malformed word, prefix with '0x' to use hexadecimal format."
3080 return
3081 print "Searching for word %d/0x%s:" % (word, self.reader.FormatIntPtr(word))
3082 self.reader.FindWord(word)
3083
3084 def do_sh(self, none):
3085 """
3086 Search for the V8 Heap object in all available memory regions. You
3087 might get lucky and find this rare treasure full of invaluable
3088 information.
3089 """
3090 raise NotImplementedError
3091
3092 def do_u(self, args):
3093 """
3094 Unassemble memory in the region [address, address + size). If the
3095 size is not specified, a default value of 32 bytes is used.
3096 Synopsis: u 0x<address> 0x<size>
3097 """
3098 args = args.split(' ')
3099 start = int(args[0], 16)
3100 size = int(args[1], 16) if len(args) > 1 else 0x20
3101 if not self.reader.IsValidAddress(start):
3102 print "Address is not contained within the minidump!"
3103 return
3104 lines = self.reader.GetDisasmLines(start, size)
3105 for line in lines:
3106 print FormatDisasmLine(start, self.heap, line)
3107 print
3108
3109 def do_EOF(self, none):
3110 raise KeyboardInterrupt
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003111
3112EIP_PROXIMITY = 64
3113
Ben Murdoch3ef787d2012-04-12 10:51:47 +01003114CONTEXT_FOR_ARCH = {
3115 MD_CPU_ARCHITECTURE_AMD64:
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003116 ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip',
3117 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'],
3118 MD_CPU_ARCHITECTURE_ARM:
3119 ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
3120 'r10', 'r11', 'r12', 'sp', 'lr', 'pc'],
Ben Murdoch014dc512016-03-22 12:00:34 +00003121 MD_CPU_ARCHITECTURE_ARM64:
3122 ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
3123 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 'r16', 'r17', 'r18', 'r19',
3124 'r20', 'r21', 'r22', 'r23', 'r24', 'r25', 'r26', 'r27', 'r28',
3125 'fp', 'lr', 'sp', 'pc'],
Ben Murdoch3ef787d2012-04-12 10:51:47 +01003126 MD_CPU_ARCHITECTURE_X86:
3127 ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
3128}
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003129
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003130KNOWN_MODULES = {'chrome.exe', 'chrome.dll'}
3131
3132def GetVersionString(ms, ls):
3133 return "%d.%d.%d.%d" % (ms >> 16, ms & 0xffff, ls >> 16, ls & 0xffff)
3134
3135
3136def GetModuleName(reader, module):
3137 name = reader.ReadMinidumpString(module.module_name_rva)
3138 # simplify for path manipulation
3139 name = name.encode('utf-8')
3140 return str(os.path.basename(str(name).replace("\\", "/")))
3141
3142
3143def PrintModuleDetails(reader, module):
3144 print "%s" % GetModuleName(reader, module)
3145 file_version = GetVersionString(module.version_info.dwFileVersionMS,
3146 module.version_info.dwFileVersionLS)
3147 product_version = GetVersionString(module.version_info.dwProductVersionMS,
3148 module.version_info.dwProductVersionLS)
3149 print " base: %s" % reader.FormatIntPtr(module.base_of_image)
3150 print " end: %s" % reader.FormatIntPtr(module.base_of_image +
3151 module.size_of_image)
3152 print " file version: %s" % file_version
3153 print " product version: %s" % product_version
3154 time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
3155 print " timestamp: %s" % time_date_stamp
3156
3157
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003158def AnalyzeMinidump(options, minidump_name):
3159 reader = MinidumpReader(options, minidump_name)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003160 heap = None
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003161 DebugPrint("========================================")
3162 if reader.exception is None:
3163 print "Minidump has no exception info"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003164 else:
3165 print "Exception info:"
3166 exception_thread = reader.thread_map[reader.exception.thread_id]
3167 print " thread id: %d" % exception_thread.id
3168 print " code: %08X" % reader.exception.exception.code
3169 print " context:"
3170 for r in CONTEXT_FOR_ARCH[reader.arch]:
3171 print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r)))
3172 # TODO(vitalyr): decode eflags.
Ben Murdoch014dc512016-03-22 12:00:34 +00003173 if reader.arch in [MD_CPU_ARCHITECTURE_ARM, MD_CPU_ARCHITECTURE_ARM64]:
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003174 print " cpsr: %s" % bin(reader.exception_context.cpsr)[2:]
3175 else:
3176 print " eflags: %s" % bin(reader.exception_context.eflags)[2:]
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003177
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003178 print
3179 print " modules:"
3180 for module in reader.module_list.modules:
3181 name = GetModuleName(reader, module)
3182 if name in KNOWN_MODULES:
3183 print " %s at %08X" % (name, module.base_of_image)
3184 reader.TryLoadSymbolsFor(name, module)
3185 print
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003186
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003187 stack_top = reader.ExceptionSP()
3188 stack_bottom = exception_thread.stack.start + \
3189 exception_thread.stack.memory.data_size
3190 stack_map = {reader.ExceptionIP(): -1}
3191 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
3192 maybe_address = reader.ReadUIntPtr(slot)
3193 if not maybe_address in stack_map:
3194 stack_map[maybe_address] = slot
3195 heap = V8Heap(reader, stack_map)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003196
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003197 print "Disassembly around exception.eip:"
3198 eip_symbol = reader.FindSymbol(reader.ExceptionIP())
3199 if eip_symbol is not None:
3200 print eip_symbol
3201 disasm_start = reader.ExceptionIP() - EIP_PROXIMITY
3202 disasm_bytes = 2 * EIP_PROXIMITY
3203 if (options.full):
3204 full_range = reader.FindRegion(reader.ExceptionIP())
3205 if full_range is not None:
3206 disasm_start = full_range[0]
3207 disasm_bytes = full_range[1]
3208
3209 lines = reader.GetDisasmLines(disasm_start, disasm_bytes)
3210
Ben Murdoch014dc512016-03-22 12:00:34 +00003211 if not lines:
3212 print "Could not disassemble using %s." % OBJDUMP_BIN
3213 print "Pass path to architecture specific objdump via --objdump?"
3214
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003215 for line in lines:
3216 print FormatDisasmLine(disasm_start, heap, line)
3217 print
3218
3219 if heap is None:
3220 heap = V8Heap(reader, None)
3221
3222 if options.full:
3223 FullDump(reader, heap)
3224
3225 if options.command:
3226 InspectionShell(reader, heap).onecmd(options.command)
3227
3228 if options.shell:
3229 try:
3230 InspectionShell(reader, heap).cmdloop("type help to get help")
3231 except KeyboardInterrupt:
3232 print "Kthxbye."
3233 elif not options.command:
3234 if reader.exception is not None:
3235 frame_pointer = reader.ExceptionFP()
Ben Murdoch014dc512016-03-22 12:00:34 +00003236 in_oom_dump_area = False
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003237 print "Annotated stack (from exception.esp to bottom):"
3238 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
3239 ascii_content = [c if c >= '\x20' and c < '\x7f' else '.'
3240 for c in reader.ReadBytes(slot, reader.PointerSize())]
3241 maybe_address = reader.ReadUIntPtr(slot)
Ben Murdoch014dc512016-03-22 12:00:34 +00003242 maybe_address_contents = None
3243 if maybe_address >= stack_top and maybe_address <= stack_bottom:
3244 maybe_address_contents = reader.ReadUIntPtr(maybe_address)
3245 if maybe_address_contents == 0xdecade00:
3246 in_oom_dump_area = True
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003247 heap_object = heap.FindObject(maybe_address)
3248 maybe_symbol = reader.FindSymbol(maybe_address)
Ben Murdoch014dc512016-03-22 12:00:34 +00003249 oom_comment = ""
3250 if in_oom_dump_area:
3251 if maybe_address_contents == 0xdecade00:
3252 oom_comment = " <----- HeapStats start marker"
3253 elif maybe_address_contents == 0xdecade01:
3254 oom_comment = " <----- HeapStats end marker"
3255 elif maybe_address_contents is not None:
3256 oom_comment = " %d (%d Mbytes)" % (maybe_address_contents,
3257 maybe_address_contents >> 20)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003258 if slot == frame_pointer:
3259 maybe_symbol = "<---- frame pointer"
3260 frame_pointer = maybe_address
Ben Murdoch014dc512016-03-22 12:00:34 +00003261 print "%s: %s %s %s%s" % (reader.FormatIntPtr(slot),
3262 reader.FormatIntPtr(maybe_address),
3263 "".join(ascii_content),
3264 maybe_symbol or "",
3265 oom_comment)
3266 if maybe_address_contents == 0xdecade01:
3267 in_oom_dump_area = False
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003268 if heap_object:
3269 heap_object.Print(Printer())
3270 print
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003271
3272 reader.Dispose()
3273
3274
3275if __name__ == "__main__":
3276 parser = optparse.OptionParser(USAGE)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003277 parser.add_option("-s", "--shell", dest="shell", action="store_true",
3278 help="start an interactive inspector shell")
3279 parser.add_option("-w", "--web", dest="web", action="store_true",
3280 help="start a web server on localhost:%i" % PORT_NUMBER)
3281 parser.add_option("-c", "--command", dest="command", default="",
3282 help="run an interactive inspector shell command and exit")
3283 parser.add_option("-f", "--full", dest="full", action="store_true",
3284 help="dump all information contained in the minidump")
3285 parser.add_option("--symdir", dest="symdir", default=".",
3286 help="directory containing *.pdb.sym file with symbols")
3287 parser.add_option("--objdump",
3288 default="/usr/bin/objdump",
3289 help="objdump tool to use [default: %default]")
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003290 options, args = parser.parse_args()
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003291 if os.path.exists(options.objdump):
3292 disasm.OBJDUMP_BIN = options.objdump
3293 OBJDUMP_BIN = options.objdump
3294 else:
3295 print "Cannot find %s, falling back to default objdump" % options.objdump
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003296 if len(args) != 1:
3297 parser.print_help()
3298 sys.exit(1)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003299 if options.web:
3300 try:
3301 server = InspectionWebServer(PORT_NUMBER, options, args[0])
3302 print 'Started httpserver on port ' , PORT_NUMBER
3303 webbrowser.open('http://localhost:%i/summary.html' % PORT_NUMBER)
3304 server.serve_forever()
3305 except KeyboardInterrupt:
3306 print '^C received, shutting down the web server'
3307 server.socket.close()
3308 else:
3309 AnalyzeMinidump(options, args[0])