blob: 2177ec2122aa9824df335dc7b212eb74c171f564 [file] [log] [blame]
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001#!/usr/bin/env python
2#
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003# Copyright 2012 the V8 project authors. All rights reserved.
Ben Murdoche0cee9b2011-05-25 10:26:03 +01004# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met:
7#
8# * Redistributions of source code must retain the above copyright
9# notice, this list of conditions and the following disclaimer.
10# * Redistributions in binary form must reproduce the above
11# copyright notice, this list of conditions and the following
12# disclaimer in the documentation and/or other materials provided
13# with the distribution.
14# * Neither the name of Google Inc. nor the names of its
15# contributors may be used to endorse or promote products derived
16# from this software without specific prior written permission.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
Ben Murdochb8a8cc12014-11-26 15:28:44 +000030import BaseHTTPServer
31import bisect
32import cgi
33import cmd
34import codecs
Ben Murdoche0cee9b2011-05-25 10:26:03 +010035import ctypes
Ben Murdochb8a8cc12014-11-26 15:28:44 +000036import datetime
37import disasm
Ben Murdoche0cee9b2011-05-25 10:26:03 +010038import mmap
39import optparse
40import os
Ben Murdochb8a8cc12014-11-26 15:28:44 +000041import re
Ben Murdoche0cee9b2011-05-25 10:26:03 +010042import sys
43import types
Ben Murdochb8a8cc12014-11-26 15:28:44 +000044import urllib
45import urlparse
46import v8heapconst
47import webbrowser
48
49PORT_NUMBER = 8081
Ben Murdoche0cee9b2011-05-25 10:26:03 +010050
51
Ben Murdochb8a8cc12014-11-26 15:28:44 +000052USAGE="""usage: %prog [OPTIONS] [DUMP-FILE]
Ben Murdoche0cee9b2011-05-25 10:26:03 +010053
54Minidump analyzer.
55
56Shows the processor state at the point of exception including the
57stack of the active thread and the referenced objects in the V8
58heap. Code objects are disassembled and the addresses linked from the
Ben Murdochb8a8cc12014-11-26 15:28:44 +000059stack (e.g. pushed return addresses) are marked with "=>".
Ben Murdoche0cee9b2011-05-25 10:26:03 +010060
61Examples:
Ben Murdochb8a8cc12014-11-26 15:28:44 +000062 $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp"""
Ben Murdoche0cee9b2011-05-25 10:26:03 +010063
Ben Murdoch3ef787d2012-04-12 10:51:47 +010064
Ben Murdoche0cee9b2011-05-25 10:26:03 +010065DEBUG=False
66
67
68def DebugPrint(s):
69 if not DEBUG: return
70 print s
71
72
73class Descriptor(object):
74 """Descriptor of a structure in a memory."""
75
76 def __init__(self, fields):
77 self.fields = fields
78 self.is_flexible = False
79 for _, type_or_func in fields:
80 if isinstance(type_or_func, types.FunctionType):
81 self.is_flexible = True
82 break
83 if not self.is_flexible:
84 self.ctype = Descriptor._GetCtype(fields)
85 self.size = ctypes.sizeof(self.ctype)
86
87 def Read(self, memory, offset):
88 if self.is_flexible:
89 fields_copy = self.fields[:]
90 last = 0
91 for name, type_or_func in fields_copy:
92 if isinstance(type_or_func, types.FunctionType):
93 partial_ctype = Descriptor._GetCtype(fields_copy[:last])
94 partial_object = partial_ctype.from_buffer(memory, offset)
95 type = type_or_func(partial_object)
96 if type is not None:
97 fields_copy[last] = (name, type)
98 last += 1
99 else:
100 last += 1
101 complete_ctype = Descriptor._GetCtype(fields_copy[:last])
102 else:
103 complete_ctype = self.ctype
104 return complete_ctype.from_buffer(memory, offset)
105
106 @staticmethod
107 def _GetCtype(fields):
108 class Raw(ctypes.Structure):
109 _fields_ = fields
110 _pack_ = 1
111
112 def __str__(self):
113 return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field))
114 for field, _ in Raw._fields_) + "}"
115 return Raw
116
117
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000118def FullDump(reader, heap):
119 """Dump all available memory regions."""
120 def dump_region(reader, start, size, location):
121 print
122 while start & 3 != 0:
123 start += 1
124 size -= 1
125 location += 1
126 is_executable = reader.IsProbableExecutableRegion(location, size)
127 is_ascii = reader.IsProbableASCIIRegion(location, size)
128
129 if is_executable is not False:
130 lines = reader.GetDisasmLines(start, size)
131 for line in lines:
132 print FormatDisasmLine(start, heap, line)
133 print
134
135 if is_ascii is not False:
136 # Output in the same format as the Unix hd command
137 addr = start
138 for slot in xrange(location, location + size, 16):
139 hex_line = ""
140 asc_line = ""
141 for i in xrange(0, 16):
142 if slot + i < location + size:
143 byte = ctypes.c_uint8.from_buffer(reader.minidump, slot + i).value
144 if byte >= 0x20 and byte < 0x7f:
145 asc_line += chr(byte)
146 else:
147 asc_line += "."
148 hex_line += " %02x" % (byte)
149 else:
150 hex_line += " "
151 if i == 7:
152 hex_line += " "
153 print "%s %s |%s|" % (reader.FormatIntPtr(addr),
154 hex_line,
155 asc_line)
156 addr += 16
157
158 if is_executable is not True and is_ascii is not True:
159 print "%s - %s" % (reader.FormatIntPtr(start),
160 reader.FormatIntPtr(start + size))
161 for slot in xrange(start,
162 start + size,
163 reader.PointerSize()):
164 maybe_address = reader.ReadUIntPtr(slot)
165 heap_object = heap.FindObject(maybe_address)
166 print "%s: %s" % (reader.FormatIntPtr(slot),
167 reader.FormatIntPtr(maybe_address))
168 if heap_object:
169 heap_object.Print(Printer())
170 print
171
172 reader.ForEachMemoryRegion(dump_region)
173
174# Heap constants generated by 'make grokdump' in v8heapconst module.
175INSTANCE_TYPES = v8heapconst.INSTANCE_TYPES
176KNOWN_MAPS = v8heapconst.KNOWN_MAPS
177KNOWN_OBJECTS = v8heapconst.KNOWN_OBJECTS
178
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100179# Set of structures and constants that describe the layout of minidump
180# files. Based on MSDN and Google Breakpad.
181
182MINIDUMP_HEADER = Descriptor([
183 ("signature", ctypes.c_uint32),
184 ("version", ctypes.c_uint32),
185 ("stream_count", ctypes.c_uint32),
186 ("stream_directories_rva", ctypes.c_uint32),
187 ("checksum", ctypes.c_uint32),
188 ("time_date_stampt", ctypes.c_uint32),
189 ("flags", ctypes.c_uint64)
190])
191
192MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
193 ("data_size", ctypes.c_uint32),
194 ("rva", ctypes.c_uint32)
195])
196
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000197MINIDUMP_STRING = Descriptor([
198 ("length", ctypes.c_uint32),
199 ("buffer", lambda t: ctypes.c_uint8 * (t.length + 2))
200])
201
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100202MINIDUMP_DIRECTORY = Descriptor([
203 ("stream_type", ctypes.c_uint32),
204 ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
205])
206
207MD_EXCEPTION_MAXIMUM_PARAMETERS = 15
208
209MINIDUMP_EXCEPTION = Descriptor([
210 ("code", ctypes.c_uint32),
211 ("flags", ctypes.c_uint32),
212 ("record", ctypes.c_uint64),
213 ("address", ctypes.c_uint64),
214 ("parameter_count", ctypes.c_uint32),
215 ("unused_alignment", ctypes.c_uint32),
216 ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS)
217])
218
219MINIDUMP_EXCEPTION_STREAM = Descriptor([
220 ("thread_id", ctypes.c_uint32),
221 ("unused_alignment", ctypes.c_uint32),
222 ("exception", MINIDUMP_EXCEPTION.ctype),
223 ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
224])
225
226# Stream types.
227MD_UNUSED_STREAM = 0
228MD_RESERVED_STREAM_0 = 1
229MD_RESERVED_STREAM_1 = 2
230MD_THREAD_LIST_STREAM = 3
231MD_MODULE_LIST_STREAM = 4
232MD_MEMORY_LIST_STREAM = 5
233MD_EXCEPTION_STREAM = 6
234MD_SYSTEM_INFO_STREAM = 7
235MD_THREAD_EX_LIST_STREAM = 8
236MD_MEMORY_64_LIST_STREAM = 9
237MD_COMMENT_STREAM_A = 10
238MD_COMMENT_STREAM_W = 11
239MD_HANDLE_DATA_STREAM = 12
240MD_FUNCTION_TABLE_STREAM = 13
241MD_UNLOADED_MODULE_LIST_STREAM = 14
242MD_MISC_INFO_STREAM = 15
243MD_MEMORY_INFO_LIST_STREAM = 16
244MD_THREAD_INFO_LIST_STREAM = 17
245MD_HANDLE_OPERATION_LIST_STREAM = 18
246
247MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80
248
249MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([
250 ("control_word", ctypes.c_uint32),
251 ("status_word", ctypes.c_uint32),
252 ("tag_word", ctypes.c_uint32),
253 ("error_offset", ctypes.c_uint32),
254 ("error_selector", ctypes.c_uint32),
255 ("data_offset", ctypes.c_uint32),
256 ("data_selector", ctypes.c_uint32),
257 ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE),
258 ("cr0_npx_state", ctypes.c_uint32)
259])
260
261MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512
262
263# Context flags.
264MD_CONTEXT_X86 = 0x00010000
265MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001)
266MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002)
267MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004)
268MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008)
269MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010)
270MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020)
271
272def EnableOnFlag(type, flag):
273 return lambda o: [None, type][int((o.context_flags & flag) != 0)]
274
275MINIDUMP_CONTEXT_X86 = Descriptor([
276 ("context_flags", ctypes.c_uint32),
277 # MD_CONTEXT_X86_DEBUG_REGISTERS.
278 ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
279 ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
280 ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
281 ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
282 ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
283 ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
284 # MD_CONTEXT_X86_FLOATING_POINT.
285 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype,
286 MD_CONTEXT_X86_FLOATING_POINT)),
287 # MD_CONTEXT_X86_SEGMENTS.
288 ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
289 ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
290 ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
291 ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
292 # MD_CONTEXT_X86_INTEGER.
293 ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
294 ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
295 ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
296 ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
297 ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
298 ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
299 # MD_CONTEXT_X86_CONTROL.
300 ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
301 ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
302 ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
303 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
304 ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
305 ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
306 # MD_CONTEXT_X86_EXTENDED_REGISTERS.
307 ("extended_registers",
308 EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE,
309 MD_CONTEXT_X86_EXTENDED_REGISTERS))
310])
311
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000312MD_CONTEXT_ARM = 0x40000000
313MD_CONTEXT_ARM_INTEGER = (MD_CONTEXT_ARM | 0x00000002)
314MD_CONTEXT_ARM_FLOATING_POINT = (MD_CONTEXT_ARM | 0x00000004)
315MD_FLOATINGSAVEAREA_ARM_FPR_COUNT = 32
316MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT = 8
317
318MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([
319 ("fpscr", ctypes.c_uint64),
320 ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPR_COUNT),
321 ("extra", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT)
322])
323
324MINIDUMP_CONTEXT_ARM = Descriptor([
325 ("context_flags", ctypes.c_uint32),
326 # MD_CONTEXT_ARM_INTEGER.
327 ("r0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
328 ("r1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
329 ("r2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
330 ("r3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
331 ("r4", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
332 ("r5", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
333 ("r6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
334 ("r7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
335 ("r8", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
336 ("r9", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
337 ("r10", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
338 ("r11", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
339 ("r12", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
340 ("sp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
341 ("lr", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
342 ("pc", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
343 ("cpsr", ctypes.c_uint32),
344 ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype,
345 MD_CONTEXT_ARM_FLOATING_POINT))
346])
347
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100348MD_CONTEXT_AMD64 = 0x00100000
349MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001)
350MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002)
351MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004)
352MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008)
353MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010)
354
355MINIDUMP_CONTEXT_AMD64 = Descriptor([
356 ("p1_home", ctypes.c_uint64),
357 ("p2_home", ctypes.c_uint64),
358 ("p3_home", ctypes.c_uint64),
359 ("p4_home", ctypes.c_uint64),
360 ("p5_home", ctypes.c_uint64),
361 ("p6_home", ctypes.c_uint64),
362 ("context_flags", ctypes.c_uint32),
363 ("mx_csr", ctypes.c_uint32),
364 # MD_CONTEXT_AMD64_CONTROL.
365 ("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
366 # MD_CONTEXT_AMD64_SEGMENTS
367 ("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
368 ("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
369 ("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
370 ("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
371 # MD_CONTEXT_AMD64_CONTROL.
372 ("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
373 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)),
374 # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
375 ("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
376 ("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
377 ("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
378 ("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
379 ("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
380 ("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
381 # MD_CONTEXT_AMD64_INTEGER.
382 ("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
383 ("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
384 ("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
385 ("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
386 # MD_CONTEXT_AMD64_CONTROL.
387 ("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
388 # MD_CONTEXT_AMD64_INTEGER.
389 ("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
390 ("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
391 ("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
392 ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
393 ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
394 ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
395 ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
396 ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
397 ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
398 ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
399 ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
400 # MD_CONTEXT_AMD64_CONTROL.
401 ("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
402 # MD_CONTEXT_AMD64_FLOATING_POINT
403 ("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
404 MD_CONTEXT_AMD64_FLOATING_POINT)),
405 ("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
406 MD_CONTEXT_AMD64_FLOATING_POINT)),
407 ("vector_control", EnableOnFlag(ctypes.c_uint64,
408 MD_CONTEXT_AMD64_FLOATING_POINT)),
409 # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
410 ("debug_control", EnableOnFlag(ctypes.c_uint64,
411 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
412 ("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64,
413 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
414 ("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64,
415 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
416 ("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64,
417 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
418 ("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64,
419 MD_CONTEXT_AMD64_DEBUG_REGISTERS))
420])
421
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100422MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
423 ("start", ctypes.c_uint64),
424 ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
425])
426
427MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
428 ("start", ctypes.c_uint64),
429 ("size", ctypes.c_uint64)
430])
431
432MINIDUMP_MEMORY_LIST = Descriptor([
433 ("range_count", ctypes.c_uint32),
434 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
435])
436
437MINIDUMP_MEMORY_LIST64 = Descriptor([
438 ("range_count", ctypes.c_uint64),
439 ("base_rva", ctypes.c_uint64),
440 ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count)
441])
442
443MINIDUMP_THREAD = Descriptor([
444 ("id", ctypes.c_uint32),
445 ("suspend_count", ctypes.c_uint32),
446 ("priority_class", ctypes.c_uint32),
447 ("priority", ctypes.c_uint32),
448 ("ted", ctypes.c_uint64),
449 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype),
450 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
451])
452
453MINIDUMP_THREAD_LIST = Descriptor([
454 ("thread_count", ctypes.c_uint32),
455 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
456])
457
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000458MINIDUMP_VS_FIXEDFILEINFO = Descriptor([
459 ("dwSignature", ctypes.c_uint32),
460 ("dwStrucVersion", ctypes.c_uint32),
461 ("dwFileVersionMS", ctypes.c_uint32),
462 ("dwFileVersionLS", ctypes.c_uint32),
463 ("dwProductVersionMS", ctypes.c_uint32),
464 ("dwProductVersionLS", ctypes.c_uint32),
465 ("dwFileFlagsMask", ctypes.c_uint32),
466 ("dwFileFlags", ctypes.c_uint32),
467 ("dwFileOS", ctypes.c_uint32),
468 ("dwFileType", ctypes.c_uint32),
469 ("dwFileSubtype", ctypes.c_uint32),
470 ("dwFileDateMS", ctypes.c_uint32),
471 ("dwFileDateLS", ctypes.c_uint32)
472])
473
474MINIDUMP_RAW_MODULE = Descriptor([
475 ("base_of_image", ctypes.c_uint64),
476 ("size_of_image", ctypes.c_uint32),
477 ("checksum", ctypes.c_uint32),
478 ("time_date_stamp", ctypes.c_uint32),
479 ("module_name_rva", ctypes.c_uint32),
480 ("version_info", MINIDUMP_VS_FIXEDFILEINFO.ctype),
481 ("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
482 ("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
483 ("reserved0", ctypes.c_uint32 * 2),
484 ("reserved1", ctypes.c_uint32 * 2)
485])
486
487MINIDUMP_MODULE_LIST = Descriptor([
488 ("number_of_modules", ctypes.c_uint32),
489 ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules)
490])
491
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100492MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
493 ("processor_architecture", ctypes.c_uint16)
494])
495
496MD_CPU_ARCHITECTURE_X86 = 0
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000497MD_CPU_ARCHITECTURE_ARM = 5
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100498MD_CPU_ARCHITECTURE_AMD64 = 9
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100499
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000500class FuncSymbol:
501 def __init__(self, start, size, name):
502 self.start = start
503 self.end = self.start + size
504 self.name = name
505
506 def __cmp__(self, other):
507 if isinstance(other, FuncSymbol):
508 return self.start - other.start
509 return self.start - other
510
511 def Covers(self, addr):
512 return (self.start <= addr) and (addr < self.end)
513
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100514class MinidumpReader(object):
515 """Minidump (.dmp) reader."""
516
517 _HEADER_MAGIC = 0x504d444d
518
519 def __init__(self, options, minidump_name):
520 self.minidump_name = minidump_name
521 self.minidump_file = open(minidump_name, "r")
522 self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE)
523 self.header = MINIDUMP_HEADER.Read(self.minidump, 0)
524 if self.header.signature != MinidumpReader._HEADER_MAGIC:
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000525 print >>sys.stderr, "Warning: Unsupported minidump header magic!"
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100526 DebugPrint(self.header)
527 directories = []
528 offset = self.header.stream_directories_rva
529 for _ in xrange(self.header.stream_count):
530 directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
531 offset += MINIDUMP_DIRECTORY.size
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100532 self.arch = None
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100533 self.exception = None
534 self.exception_context = None
535 self.memory_list = None
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000536 self.memory_list64 = None
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000537 self.module_list = None
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100538 self.thread_map = {}
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100539
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000540 self.symdir = options.symdir
541 self.modules_with_symbols = []
542 self.symbols = []
543
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100544 # Find MDRawSystemInfo stream and determine arch.
545 for d in directories:
546 if d.stream_type == MD_SYSTEM_INFO_STREAM:
547 system_info = MINIDUMP_RAW_SYSTEM_INFO.Read(
548 self.minidump, d.location.rva)
549 self.arch = system_info.processor_architecture
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000550 assert self.arch in [MD_CPU_ARCHITECTURE_AMD64,
551 MD_CPU_ARCHITECTURE_ARM,
552 MD_CPU_ARCHITECTURE_X86]
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100553 assert not self.arch is None
554
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100555 for d in directories:
556 DebugPrint(d)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100557 if d.stream_type == MD_EXCEPTION_STREAM:
558 self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
559 self.minidump, d.location.rva)
560 DebugPrint(self.exception)
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100561 if self.arch == MD_CPU_ARCHITECTURE_X86:
562 self.exception_context = MINIDUMP_CONTEXT_X86.Read(
563 self.minidump, self.exception.thread_context.rva)
564 elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
565 self.exception_context = MINIDUMP_CONTEXT_AMD64.Read(
566 self.minidump, self.exception.thread_context.rva)
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000567 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
568 self.exception_context = MINIDUMP_CONTEXT_ARM.Read(
569 self.minidump, self.exception.thread_context.rva)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100570 DebugPrint(self.exception_context)
571 elif d.stream_type == MD_THREAD_LIST_STREAM:
572 thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
573 assert ctypes.sizeof(thread_list) == d.location.data_size
574 DebugPrint(thread_list)
575 for thread in thread_list.threads:
576 DebugPrint(thread)
577 self.thread_map[thread.id] = thread
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000578 elif d.stream_type == MD_MODULE_LIST_STREAM:
579 assert self.module_list is None
580 self.module_list = MINIDUMP_MODULE_LIST.Read(
581 self.minidump, d.location.rva)
582 assert ctypes.sizeof(self.module_list) == d.location.data_size
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100583 elif d.stream_type == MD_MEMORY_LIST_STREAM:
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000584 print >>sys.stderr, "Warning: This is not a full minidump!"
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100585 assert self.memory_list is None
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000586 self.memory_list = MINIDUMP_MEMORY_LIST.Read(
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100587 self.minidump, d.location.rva)
588 assert ctypes.sizeof(self.memory_list) == d.location.data_size
589 DebugPrint(self.memory_list)
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000590 elif d.stream_type == MD_MEMORY_64_LIST_STREAM:
591 assert self.memory_list64 is None
592 self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read(
593 self.minidump, d.location.rva)
594 assert ctypes.sizeof(self.memory_list64) == d.location.data_size
595 DebugPrint(self.memory_list64)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100596
597 def IsValidAddress(self, address):
598 return self.FindLocation(address) is not None
599
600 def ReadU8(self, address):
601 location = self.FindLocation(address)
602 return ctypes.c_uint8.from_buffer(self.minidump, location).value
603
604 def ReadU32(self, address):
605 location = self.FindLocation(address)
606 return ctypes.c_uint32.from_buffer(self.minidump, location).value
607
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100608 def ReadU64(self, address):
609 location = self.FindLocation(address)
610 return ctypes.c_uint64.from_buffer(self.minidump, location).value
611
612 def ReadUIntPtr(self, address):
613 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
614 return self.ReadU64(address)
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000615 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
616 return self.ReadU32(address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100617 elif self.arch == MD_CPU_ARCHITECTURE_X86:
618 return self.ReadU32(address)
619
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100620 def ReadBytes(self, address, size):
621 location = self.FindLocation(address)
622 return self.minidump[location:location + size]
623
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000624 def _ReadWord(self, location):
625 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
626 return ctypes.c_uint64.from_buffer(self.minidump, location).value
627 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
628 return ctypes.c_uint32.from_buffer(self.minidump, location).value
629 elif self.arch == MD_CPU_ARCHITECTURE_X86:
630 return ctypes.c_uint32.from_buffer(self.minidump, location).value
631
632 def IsProbableASCIIRegion(self, location, length):
633 ascii_bytes = 0
634 non_ascii_bytes = 0
635 for loc in xrange(location, location + length):
636 byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
637 if byte >= 0x7f:
638 non_ascii_bytes += 1
639 if byte < 0x20 and byte != 0:
640 non_ascii_bytes += 1
641 if byte < 0x7f and byte >= 0x20:
642 ascii_bytes += 1
643 if byte == 0xa: # newline
644 ascii_bytes += 1
645 if ascii_bytes * 10 <= length:
646 return False
647 if length > 0 and ascii_bytes > non_ascii_bytes * 7:
648 return True
649 if ascii_bytes > non_ascii_bytes * 3:
650 return None # Maybe
651 return False
652
653 def IsProbableExecutableRegion(self, location, length):
654 opcode_bytes = 0
655 sixty_four = self.arch == MD_CPU_ARCHITECTURE_AMD64
656 for loc in xrange(location, location + length):
657 byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
658 if (byte == 0x8b or # mov
659 byte == 0x89 or # mov reg-reg
660 (byte & 0xf0) == 0x50 or # push/pop
661 (sixty_four and (byte & 0xf0) == 0x40) or # rex prefix
662 byte == 0xc3 or # return
663 byte == 0x74 or # jeq
664 byte == 0x84 or # jeq far
665 byte == 0x75 or # jne
666 byte == 0x85 or # jne far
667 byte == 0xe8 or # call
668 byte == 0xe9 or # jmp far
669 byte == 0xeb): # jmp near
670 opcode_bytes += 1
671 opcode_percent = (opcode_bytes * 100) / length
672 threshold = 20
673 if opcode_percent > threshold + 2:
674 return True
675 if opcode_percent > threshold - 2:
676 return None # Maybe
677 return False
678
679 def FindRegion(self, addr):
680 answer = [-1, -1]
681 def is_in(reader, start, size, location):
682 if addr >= start and addr < start + size:
683 answer[0] = start
684 answer[1] = size
685 self.ForEachMemoryRegion(is_in)
686 if answer[0] == -1:
687 return None
688 return answer
689
690 def ForEachMemoryRegion(self, cb):
691 if self.memory_list64 is not None:
692 for r in self.memory_list64.ranges:
693 location = self.memory_list64.base_rva + offset
694 cb(self, r.start, r.size, location)
695 offset += r.size
696
697 if self.memory_list is not None:
698 for r in self.memory_list.ranges:
699 cb(self, r.start, r.memory.data_size, r.memory.rva)
700
701 def FindWord(self, word, alignment=0):
702 def search_inside_region(reader, start, size, location):
703 location = (location + alignment) & ~alignment
704 for loc in xrange(location, location + size - self.PointerSize()):
705 if reader._ReadWord(loc) == word:
706 slot = start + (loc - location)
707 print "%s: %s" % (reader.FormatIntPtr(slot),
708 reader.FormatIntPtr(word))
709 self.ForEachMemoryRegion(search_inside_region)
710
711 def FindWordList(self, word):
712 aligned_res = []
713 unaligned_res = []
714 def search_inside_region(reader, start, size, location):
715 for loc in xrange(location, location + size - self.PointerSize()):
716 if reader._ReadWord(loc) == word:
717 slot = start + (loc - location)
718 if slot % self.PointerSize() == 0:
719 aligned_res.append(slot)
720 else:
721 unaligned_res.append(slot)
722 self.ForEachMemoryRegion(search_inside_region)
723 return (aligned_res, unaligned_res)
724
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100725 def FindLocation(self, address):
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100726 offset = 0
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000727 if self.memory_list64 is not None:
728 for r in self.memory_list64.ranges:
729 if r.start <= address < r.start + r.size:
730 return self.memory_list64.base_rva + offset + address - r.start
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000731 offset += r.size
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000732 if self.memory_list is not None:
733 for r in self.memory_list.ranges:
734 if r.start <= address < r.start + r.memory.data_size:
735 return r.memory.rva + address - r.start
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100736 return None
737
738 def GetDisasmLines(self, address, size):
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000739 def CountUndefinedInstructions(lines):
740 pattern = "<UNDEFINED>"
741 return sum([line.count(pattern) for (ignore, line) in lines])
742
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100743 location = self.FindLocation(address)
744 if location is None: return []
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100745 arch = None
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000746 possible_objdump_flags = [""]
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100747 if self.arch == MD_CPU_ARCHITECTURE_X86:
748 arch = "ia32"
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000749 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
750 arch = "arm"
751 possible_objdump_flags = ["", "--disassembler-options=force-thumb"]
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100752 elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
753 arch = "x64"
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000754 results = [ disasm.GetDisasmLines(self.minidump_name,
755 location,
756 size,
757 arch,
758 False,
759 objdump_flags)
760 for objdump_flags in possible_objdump_flags ]
761 return min(results, key=CountUndefinedInstructions)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100762
763
764 def Dispose(self):
765 self.minidump.close()
766 self.minidump_file.close()
767
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100768 def ExceptionIP(self):
769 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
770 return self.exception_context.rip
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000771 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
772 return self.exception_context.pc
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100773 elif self.arch == MD_CPU_ARCHITECTURE_X86:
774 return self.exception_context.eip
775
776 def ExceptionSP(self):
777 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
778 return self.exception_context.rsp
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000779 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
780 return self.exception_context.sp
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100781 elif self.arch == MD_CPU_ARCHITECTURE_X86:
782 return self.exception_context.esp
783
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000784 def ExceptionFP(self):
785 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
786 return self.exception_context.rbp
787 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
788 return None
789 elif self.arch == MD_CPU_ARCHITECTURE_X86:
790 return self.exception_context.ebp
791
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100792 def FormatIntPtr(self, value):
793 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
794 return "%016x" % value
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000795 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
796 return "%08x" % value
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100797 elif self.arch == MD_CPU_ARCHITECTURE_X86:
798 return "%08x" % value
799
800 def PointerSize(self):
801 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
802 return 8
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000803 elif self.arch == MD_CPU_ARCHITECTURE_ARM:
804 return 4
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100805 elif self.arch == MD_CPU_ARCHITECTURE_X86:
806 return 4
807
808 def Register(self, name):
809 return self.exception_context.__getattribute__(name)
810
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000811 def ReadMinidumpString(self, rva):
812 string = bytearray(MINIDUMP_STRING.Read(self.minidump, rva).buffer)
813 string = string.decode("utf16")
814 return string[0:len(string) - 1]
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100815
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000816 # Load FUNC records from a BreakPad symbol file
817 #
818 # http://code.google.com/p/google-breakpad/wiki/SymbolFiles
819 #
820 def _LoadSymbolsFrom(self, symfile, baseaddr):
821 print "Loading symbols from %s" % (symfile)
822 funcs = []
823 with open(symfile) as f:
824 for line in f:
825 result = re.match(
826 r"^FUNC ([a-f0-9]+) ([a-f0-9]+) ([a-f0-9]+) (.*)$", line)
827 if result is not None:
828 start = int(result.group(1), 16)
829 size = int(result.group(2), 16)
830 name = result.group(4).rstrip()
831 bisect.insort_left(self.symbols,
832 FuncSymbol(baseaddr + start, size, name))
833 print " ... done"
834
835 def TryLoadSymbolsFor(self, modulename, module):
836 try:
837 symfile = os.path.join(self.symdir,
838 modulename.replace('.', '_') + ".pdb.sym")
839 if os.path.isfile(symfile):
840 self._LoadSymbolsFrom(symfile, module.base_of_image)
841 self.modules_with_symbols.append(module)
842 except Exception as e:
843 print " ... failure (%s)" % (e)
844
845 # Returns true if address is covered by some module that has loaded symbols.
846 def _IsInModuleWithSymbols(self, addr):
847 for module in self.modules_with_symbols:
848 start = module.base_of_image
849 end = start + module.size_of_image
850 if (start <= addr) and (addr < end):
851 return True
852 return False
853
854 # Find symbol covering the given address and return its name in format
855 # <symbol name>+<offset from the start>
856 def FindSymbol(self, addr):
857 if not self._IsInModuleWithSymbols(addr):
858 return None
859
860 i = bisect.bisect_left(self.symbols, addr)
861 symbol = None
862 if (0 < i) and self.symbols[i - 1].Covers(addr):
863 symbol = self.symbols[i - 1]
864 elif (i < len(self.symbols)) and self.symbols[i].Covers(addr):
865 symbol = self.symbols[i]
866 else:
867 return None
868 diff = addr - symbol.start
869 return "%s+0x%x" % (symbol.name, diff)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100870
871
872class Printer(object):
873 """Printer with indentation support."""
874
875 def __init__(self):
876 self.indent = 0
877
878 def Indent(self):
879 self.indent += 2
880
881 def Dedent(self):
882 self.indent -= 2
883
884 def Print(self, string):
885 print "%s%s" % (self._IndentString(), string)
886
887 def PrintLines(self, lines):
888 indent = self._IndentString()
889 print "\n".join("%s%s" % (indent, line) for line in lines)
890
891 def _IndentString(self):
892 return self.indent * " "
893
894
895ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
896
897
898def FormatDisasmLine(start, heap, line):
899 line_address = start + line[0]
900 stack_slot = heap.stack_map.get(line_address)
901 marker = " "
902 if stack_slot:
903 marker = "=>"
904 code = AnnotateAddresses(heap, line[1])
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000905
906 # Compute the actual call target which the disassembler is too stupid
907 # to figure out (it adds the call offset to the disassembly offset rather
908 # than the absolute instruction address).
909 if heap.reader.arch == MD_CPU_ARCHITECTURE_X86:
910 if code.startswith("e8"):
911 words = code.split()
912 if len(words) > 6 and words[5] == "call":
913 offset = int(words[4] + words[3] + words[2] + words[1], 16)
914 target = (line_address + offset + 5) & 0xFFFFFFFF
915 code = code.replace(words[6], "0x%08x" % target)
916 # TODO(jkummerow): port this hack to ARM and x64.
917
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100918 return "%s%08x %08x: %s" % (marker, line_address, line[0], code)
919
920
921def AnnotateAddresses(heap, line):
922 extra = []
923 for m in ADDRESS_RE.finditer(line):
924 maybe_address = int(m.group(0), 16)
925 object = heap.FindObject(maybe_address)
926 if not object: continue
927 extra.append(str(object))
928 if len(extra) == 0: return line
929 return "%s ;; %s" % (line, ", ".join(extra))
930
931
932class HeapObject(object):
933 def __init__(self, heap, map, address):
934 self.heap = heap
935 self.map = map
936 self.address = address
937
938 def Is(self, cls):
939 return isinstance(self, cls)
940
941 def Print(self, p):
942 p.Print(str(self))
943
944 def __str__(self):
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100945 return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address),
946 INSTANCE_TYPES[self.map.instance_type])
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100947
948 def ObjectField(self, offset):
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100949 field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100950 return self.heap.FindObjectOrSmi(field_value)
951
952 def SmiField(self, offset):
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100953 field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000954 if (field_value & 1) == 0:
955 return field_value / 2
956 return None
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100957
958
959class Map(HeapObject):
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000960 def Decode(self, offset, size, value):
961 return (value >> offset) & ((1 << size) - 1)
962
963 # Instance Sizes
964 def InstanceSizesOffset(self):
965 return self.heap.PointerSize()
966
967 def InstanceSizeOffset(self):
968 return self.InstanceSizesOffset()
969
970 def InObjectProperties(self):
971 return self.InstanceSizeOffset() + 1
972
973 def PreAllocatedPropertyFields(self):
974 return self.InObjectProperties() + 1
975
976 def VisitorId(self):
977 return self.PreAllocatedPropertyFields() + 1
978
979 # Instance Attributes
980 def InstanceAttributesOffset(self):
981 return self.InstanceSizesOffset() + self.heap.IntSize()
982
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100983 def InstanceTypeOffset(self):
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000984 return self.InstanceAttributesOffset()
985
986 def UnusedPropertyFieldsOffset(self):
987 return self.InstanceTypeOffset() + 1
988
989 def BitFieldOffset(self):
990 return self.UnusedPropertyFieldsOffset() + 1
991
992 def BitField2Offset(self):
993 return self.BitFieldOffset() + 1
994
995 # Other fields
996 def PrototypeOffset(self):
997 return self.InstanceAttributesOffset() + self.heap.IntSize()
998
999 def ConstructorOffset(self):
1000 return self.PrototypeOffset() + self.heap.PointerSize()
1001
1002 def TransitionsOrBackPointerOffset(self):
1003 return self.ConstructorOffset() + self.heap.PointerSize()
1004
1005 def DescriptorsOffset(self):
1006 return self.TransitionsOrBackPointerOffset() + self.heap.PointerSize()
1007
1008 def CodeCacheOffset(self):
1009 return self.DescriptorsOffset() + self.heap.PointerSize()
1010
1011 def DependentCodeOffset(self):
1012 return self.CodeCacheOffset() + self.heap.PointerSize()
1013
1014 def BitField3Offset(self):
1015 return self.DependentCodeOffset() + self.heap.PointerSize()
1016
1017 def ReadByte(self, offset):
1018 return self.heap.reader.ReadU8(self.address + offset)
1019
1020 def Print(self, p):
1021 p.Print("Map(%08x)" % (self.address))
1022 p.Print("- size: %d, inobject: %d, preallocated: %d, visitor: %d" % (
1023 self.ReadByte(self.InstanceSizeOffset()),
1024 self.ReadByte(self.InObjectProperties()),
1025 self.ReadByte(self.PreAllocatedPropertyFields()),
1026 self.VisitorId()))
1027
1028 bitfield = self.ReadByte(self.BitFieldOffset())
1029 bitfield2 = self.ReadByte(self.BitField2Offset())
1030 p.Print("- %s, unused: %d, bf: %d, bf2: %d" % (
1031 INSTANCE_TYPES[self.ReadByte(self.InstanceTypeOffset())],
1032 self.ReadByte(self.UnusedPropertyFieldsOffset()),
1033 bitfield, bitfield2))
1034
1035 p.Print("- kind: %s" % (self.Decode(3, 5, bitfield2)))
1036
1037 bitfield3 = self.ObjectField(self.BitField3Offset())
1038 p.Print(
1039 "- EnumLength: %d NumberOfOwnDescriptors: %d OwnsDescriptors: %s" % (
1040 self.Decode(0, 11, bitfield3),
1041 self.Decode(11, 11, bitfield3),
1042 self.Decode(25, 1, bitfield3)))
1043 p.Print("- IsShared: %s" % (self.Decode(22, 1, bitfield3)))
1044 p.Print("- FunctionWithPrototype: %s" % (self.Decode(23, 1, bitfield3)))
1045 p.Print("- DictionaryMap: %s" % (self.Decode(24, 1, bitfield3)))
1046
1047 descriptors = self.ObjectField(self.DescriptorsOffset())
1048 if descriptors.__class__ == FixedArray:
1049 DescriptorArray(descriptors).Print(p)
1050 else:
1051 p.Print("Descriptors: %s" % (descriptors))
1052
1053 transitions = self.ObjectField(self.TransitionsOrBackPointerOffset())
1054 if transitions.__class__ == FixedArray:
1055 TransitionArray(transitions).Print(p)
1056 else:
1057 p.Print("TransitionsOrBackPointer: %s" % (transitions))
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001058
1059 def __init__(self, heap, map, address):
1060 HeapObject.__init__(self, heap, map, address)
1061 self.instance_type = \
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001062 heap.reader.ReadU8(self.address + self.InstanceTypeOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001063
1064
1065class String(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001066 def LengthOffset(self):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001067 # First word after the map is the hash, the second is the length.
1068 return self.heap.PointerSize() * 2
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001069
1070 def __init__(self, heap, map, address):
1071 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001072 self.length = self.SmiField(self.LengthOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001073
1074 def GetChars(self):
1075 return "?string?"
1076
1077 def Print(self, p):
1078 p.Print(str(self))
1079
1080 def __str__(self):
1081 return "\"%s\"" % self.GetChars()
1082
1083
1084class SeqString(String):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001085 def CharsOffset(self):
1086 return self.heap.PointerSize() * 3
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001087
1088 def __init__(self, heap, map, address):
1089 String.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001090 self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(),
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001091 self.length)
1092
1093 def GetChars(self):
1094 return self.chars
1095
1096
1097class ExternalString(String):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001098 # TODO(vegorov) fix ExternalString for X64 architecture
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001099 RESOURCE_OFFSET = 12
1100
1101 WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
1102 WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
1103
1104 def __init__(self, heap, map, address):
1105 String.__init__(self, heap, map, address)
1106 reader = heap.reader
1107 self.resource = \
1108 reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
1109 self.chars = "?external string?"
1110 if not reader.IsValidAddress(self.resource): return
1111 string_impl_address = self.resource + \
1112 ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
1113 if not reader.IsValidAddress(string_impl_address): return
1114 string_impl = reader.ReadU32(string_impl_address)
1115 chars_ptr_address = string_impl + \
1116 ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
1117 if not reader.IsValidAddress(chars_ptr_address): return
1118 chars_ptr = reader.ReadU32(chars_ptr_address)
1119 if not reader.IsValidAddress(chars_ptr): return
1120 raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
1121 self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
1122
1123 def GetChars(self):
1124 return self.chars
1125
1126
1127class ConsString(String):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001128 def LeftOffset(self):
1129 return self.heap.PointerSize() * 3
1130
1131 def RightOffset(self):
1132 return self.heap.PointerSize() * 4
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001133
1134 def __init__(self, heap, map, address):
1135 String.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001136 self.left = self.ObjectField(self.LeftOffset())
1137 self.right = self.ObjectField(self.RightOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001138
1139 def GetChars(self):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001140 try:
1141 return self.left.GetChars() + self.right.GetChars()
1142 except:
1143 return "***CAUGHT EXCEPTION IN GROKDUMP***"
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001144
1145
1146class Oddball(HeapObject):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001147 # Should match declarations in objects.h
1148 KINDS = [
1149 "False",
1150 "True",
1151 "TheHole",
1152 "Null",
1153 "ArgumentMarker",
1154 "Undefined",
1155 "Other"
1156 ]
1157
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001158 def ToStringOffset(self):
1159 return self.heap.PointerSize()
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001160
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001161 def ToNumberOffset(self):
1162 return self.ToStringOffset() + self.heap.PointerSize()
1163
1164 def KindOffset(self):
1165 return self.ToNumberOffset() + self.heap.PointerSize()
1166
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001167 def __init__(self, heap, map, address):
1168 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001169 self.to_string = self.ObjectField(self.ToStringOffset())
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001170 self.kind = self.SmiField(self.KindOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001171
1172 def Print(self, p):
1173 p.Print(str(self))
1174
1175 def __str__(self):
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001176 if self.to_string:
1177 return "Oddball(%08x, <%s>)" % (self.address, str(self.to_string))
1178 else:
1179 kind = "???"
1180 if 0 <= self.kind < len(Oddball.KINDS):
1181 kind = Oddball.KINDS[self.kind]
1182 return "Oddball(%08x, kind=%s)" % (self.address, kind)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001183
1184
1185class FixedArray(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001186 def LengthOffset(self):
1187 return self.heap.PointerSize()
1188
1189 def ElementsOffset(self):
1190 return self.heap.PointerSize() * 2
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001191
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001192 def MemberOffset(self, i):
1193 return self.ElementsOffset() + self.heap.PointerSize() * i
1194
1195 def Get(self, i):
1196 return self.ObjectField(self.MemberOffset(i))
1197
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001198 def __init__(self, heap, map, address):
1199 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001200 self.length = self.SmiField(self.LengthOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001201
1202 def Print(self, p):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001203 p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address))
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001204 p.Indent()
1205 p.Print("length: %d" % self.length)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001206 base_offset = self.ElementsOffset()
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001207 for i in xrange(self.length):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001208 offset = base_offset + 4 * i
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001209 try:
1210 p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
1211 except TypeError:
1212 p.Dedent()
1213 p.Print("...")
1214 p.Print("}")
1215 return
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001216 p.Dedent()
1217 p.Print("}")
1218
1219 def __str__(self):
1220 return "FixedArray(%08x, length=%d)" % (self.address, self.length)
1221
1222
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001223class DescriptorArray(object):
1224 def __init__(self, array):
1225 self.array = array
1226
1227 def Length(self):
1228 return self.array.Get(0)
1229
1230 def Decode(self, offset, size, value):
1231 return (value >> offset) & ((1 << size) - 1)
1232
1233 TYPES = [
1234 "normal",
1235 "field",
1236 "function",
1237 "callbacks"
1238 ]
1239
1240 def Type(self, value):
1241 return DescriptorArray.TYPES[self.Decode(0, 3, value)]
1242
1243 def Attributes(self, value):
1244 attributes = self.Decode(3, 3, value)
1245 result = []
1246 if (attributes & 0): result += ["ReadOnly"]
1247 if (attributes & 1): result += ["DontEnum"]
1248 if (attributes & 2): result += ["DontDelete"]
1249 return "[" + (",".join(result)) + "]"
1250
1251 def Deleted(self, value):
1252 return self.Decode(6, 1, value) == 1
1253
1254 def FieldIndex(self, value):
1255 return self.Decode(20, 11, value)
1256
1257 def Pointer(self, value):
1258 return self.Decode(6, 11, value)
1259
1260 def Details(self, di, value):
1261 return (
1262 di,
1263 self.Type(value),
1264 self.Attributes(value),
1265 self.FieldIndex(value),
1266 self.Pointer(value)
1267 )
1268
1269
1270 def Print(self, p):
1271 length = self.Length()
1272 array = self.array
1273
1274 p.Print("Descriptors(%08x, length=%d)" % (array.address, length))
1275 p.Print("[et] %s" % (array.Get(1)))
1276
1277 for di in xrange(length):
1278 i = 2 + di * 3
1279 p.Print("0x%x" % (array.address + array.MemberOffset(i)))
1280 p.Print("[%i] name: %s" % (di, array.Get(i + 0)))
1281 p.Print("[%i] details: %s %s field-index %i pointer %i" % \
1282 self.Details(di, array.Get(i + 1)))
1283 p.Print("[%i] value: %s" % (di, array.Get(i + 2)))
1284
1285 end = self.array.length // 3
1286 if length != end:
1287 p.Print("[%i-%i] slack descriptors" % (length, end))
1288
1289
1290class TransitionArray(object):
1291 def __init__(self, array):
1292 self.array = array
1293
1294 def IsSimpleTransition(self):
1295 return self.array.length <= 2
1296
1297 def Length(self):
1298 # SimpleTransition cases
1299 if self.IsSimpleTransition():
1300 return self.array.length - 1
1301 return (self.array.length - 3) // 2
1302
1303 def Print(self, p):
1304 length = self.Length()
1305 array = self.array
1306
1307 p.Print("Transitions(%08x, length=%d)" % (array.address, length))
1308 p.Print("[backpointer] %s" % (array.Get(0)))
1309 if self.IsSimpleTransition():
1310 if length == 1:
1311 p.Print("[simple target] %s" % (array.Get(1)))
1312 return
1313
1314 elements = array.Get(1)
1315 if elements is not None:
1316 p.Print("[elements ] %s" % (elements))
1317
1318 prototype = array.Get(2)
1319 if prototype is not None:
1320 p.Print("[prototype ] %s" % (prototype))
1321
1322 for di in xrange(length):
1323 i = 3 + di * 2
1324 p.Print("[%i] symbol: %s" % (di, array.Get(i + 0)))
1325 p.Print("[%i] target: %s" % (di, array.Get(i + 1)))
1326
1327
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001328class JSFunction(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001329 def CodeEntryOffset(self):
1330 return 3 * self.heap.PointerSize()
1331
1332 def SharedOffset(self):
1333 return 5 * self.heap.PointerSize()
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001334
1335 def __init__(self, heap, map, address):
1336 HeapObject.__init__(self, heap, map, address)
1337 code_entry = \
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001338 heap.reader.ReadU32(self.address + self.CodeEntryOffset())
1339 self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1)
1340 self.shared = self.ObjectField(self.SharedOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001341
1342 def Print(self, p):
1343 source = "\n".join(" %s" % line for line in self._GetSource().split("\n"))
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001344 p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address))
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001345 p.Indent()
1346 p.Print("inferred name: %s" % self.shared.inferred_name)
1347 if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
1348 p.Print("script name: %s" % self.shared.script.name)
1349 p.Print("source:")
1350 p.PrintLines(self._GetSource().split("\n"))
1351 p.Print("code:")
1352 self.code.Print(p)
1353 if self.code != self.shared.code:
1354 p.Print("unoptimized code:")
1355 self.shared.code.Print(p)
1356 p.Dedent()
1357 p.Print("}")
1358
1359 def __str__(self):
1360 inferred_name = ""
1361 if self.shared.Is(SharedFunctionInfo):
1362 inferred_name = self.shared.inferred_name
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001363 return "JSFunction(%s, %s)" % \
1364 (self.heap.reader.FormatIntPtr(self.address), inferred_name)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001365
1366 def _GetSource(self):
1367 source = "?source?"
1368 start = self.shared.start_position
1369 end = self.shared.end_position
1370 if not self.shared.script.Is(Script): return source
1371 script_source = self.shared.script.source
1372 if not script_source.Is(String): return source
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001373 if start and end:
1374 source = script_source.GetChars()[start:end]
1375 return source
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001376
1377
1378class SharedFunctionInfo(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001379 def CodeOffset(self):
1380 return 2 * self.heap.PointerSize()
1381
1382 def ScriptOffset(self):
1383 return 7 * self.heap.PointerSize()
1384
1385 def InferredNameOffset(self):
1386 return 9 * self.heap.PointerSize()
1387
1388 def EndPositionOffset(self):
1389 return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
1390
1391 def StartPositionAndTypeOffset(self):
1392 return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001393
1394 def __init__(self, heap, map, address):
1395 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001396 self.code = self.ObjectField(self.CodeOffset())
1397 self.script = self.ObjectField(self.ScriptOffset())
1398 self.inferred_name = self.ObjectField(self.InferredNameOffset())
1399 if heap.PointerSize() == 8:
1400 start_position_and_type = \
1401 heap.reader.ReadU32(self.StartPositionAndTypeOffset())
1402 self.start_position = start_position_and_type >> 2
1403 pseudo_smi_end_position = \
1404 heap.reader.ReadU32(self.EndPositionOffset())
1405 self.end_position = pseudo_smi_end_position >> 2
1406 else:
1407 start_position_and_type = \
1408 self.SmiField(self.StartPositionAndTypeOffset())
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001409 if start_position_and_type:
1410 self.start_position = start_position_and_type >> 2
1411 else:
1412 self.start_position = None
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001413 self.end_position = \
1414 self.SmiField(self.EndPositionOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001415
1416
1417class Script(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001418 def SourceOffset(self):
1419 return self.heap.PointerSize()
1420
1421 def NameOffset(self):
1422 return self.SourceOffset() + self.heap.PointerSize()
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001423
1424 def __init__(self, heap, map, address):
1425 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001426 self.source = self.ObjectField(self.SourceOffset())
1427 self.name = self.ObjectField(self.NameOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001428
1429
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001430class CodeCache(HeapObject):
1431 def DefaultCacheOffset(self):
1432 return self.heap.PointerSize()
1433
1434 def NormalTypeCacheOffset(self):
1435 return self.DefaultCacheOffset() + self.heap.PointerSize()
1436
1437 def __init__(self, heap, map, address):
1438 HeapObject.__init__(self, heap, map, address)
1439 self.default_cache = self.ObjectField(self.DefaultCacheOffset())
1440 self.normal_type_cache = self.ObjectField(self.NormalTypeCacheOffset())
1441
1442 def Print(self, p):
1443 p.Print("CodeCache(%s) {" % self.heap.reader.FormatIntPtr(self.address))
1444 p.Indent()
1445 p.Print("default cache: %s" % self.default_cache)
1446 p.Print("normal type cache: %s" % self.normal_type_cache)
1447 p.Dedent()
1448 p.Print("}")
1449
1450
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001451class Code(HeapObject):
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001452 CODE_ALIGNMENT_MASK = (1 << 5) - 1
1453
1454 def InstructionSizeOffset(self):
1455 return self.heap.PointerSize()
1456
1457 @staticmethod
1458 def HeaderSize(heap):
1459 return (heap.PointerSize() + heap.IntSize() + \
1460 4 * heap.PointerSize() + 3 * heap.IntSize() + \
1461 Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001462
1463 def __init__(self, heap, map, address):
1464 HeapObject.__init__(self, heap, map, address)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001465 self.entry = self.address + Code.HeaderSize(heap)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001466 self.instruction_size = \
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001467 heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001468
1469 def Print(self, p):
1470 lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001471 p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address))
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001472 p.Indent()
1473 p.Print("instruction_size: %d" % self.instruction_size)
1474 p.PrintLines(self._FormatLine(line) for line in lines)
1475 p.Dedent()
1476 p.Print("}")
1477
1478 def _FormatLine(self, line):
1479 return FormatDisasmLine(self.entry, self.heap, line)
1480
1481
1482class V8Heap(object):
1483 CLASS_MAP = {
1484 "SYMBOL_TYPE": SeqString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001485 "ONE_BYTE_SYMBOL_TYPE": SeqString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001486 "CONS_SYMBOL_TYPE": ConsString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001487 "CONS_ONE_BYTE_SYMBOL_TYPE": ConsString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001488 "EXTERNAL_SYMBOL_TYPE": ExternalString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001489 "EXTERNAL_SYMBOL_WITH_ONE_BYTE_DATA_TYPE": ExternalString,
1490 "EXTERNAL_ONE_BYTE_SYMBOL_TYPE": ExternalString,
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001491 "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001492 "SHORT_EXTERNAL_SYMBOL_WITH_ONE_BYTE_DATA_TYPE": ExternalString,
1493 "SHORT_EXTERNAL_ONE_BYTE_SYMBOL_TYPE": ExternalString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001494 "STRING_TYPE": SeqString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001495 "ONE_BYTE_STRING_TYPE": SeqString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001496 "CONS_STRING_TYPE": ConsString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001497 "CONS_ONE_BYTE_STRING_TYPE": ConsString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001498 "EXTERNAL_STRING_TYPE": ExternalString,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001499 "EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE": ExternalString,
1500 "EXTERNAL_ONE_BYTE_STRING_TYPE": ExternalString,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001501 "MAP_TYPE": Map,
1502 "ODDBALL_TYPE": Oddball,
1503 "FIXED_ARRAY_TYPE": FixedArray,
1504 "JS_FUNCTION_TYPE": JSFunction,
1505 "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
1506 "SCRIPT_TYPE": Script,
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001507 "CODE_CACHE_TYPE": CodeCache,
1508 "CODE_TYPE": Code,
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001509 }
1510
1511 def __init__(self, reader, stack_map):
1512 self.reader = reader
1513 self.stack_map = stack_map
1514 self.objects = {}
1515
1516 def FindObjectOrSmi(self, tagged_address):
1517 if (tagged_address & 1) == 0: return tagged_address / 2
1518 return self.FindObject(tagged_address)
1519
1520 def FindObject(self, tagged_address):
1521 if tagged_address in self.objects:
1522 return self.objects[tagged_address]
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001523 if (tagged_address & self.ObjectAlignmentMask()) != 1: return None
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001524 address = tagged_address - 1
1525 if not self.reader.IsValidAddress(address): return None
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001526 map_tagged_address = self.reader.ReadUIntPtr(address)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001527 if tagged_address == map_tagged_address:
1528 # Meta map?
1529 meta_map = Map(self, None, address)
1530 instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
1531 if instance_type_name != "MAP_TYPE": return None
1532 meta_map.map = meta_map
1533 object = meta_map
1534 else:
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001535 map = self.FindMap(map_tagged_address)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001536 if map is None: return None
1537 instance_type_name = INSTANCE_TYPES.get(map.instance_type)
1538 if instance_type_name is None: return None
1539 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
1540 object = cls(self, map, address)
1541 self.objects[tagged_address] = object
1542 return object
1543
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001544 def FindMap(self, tagged_address):
1545 if (tagged_address & self.MapAlignmentMask()) != 1: return None
1546 address = tagged_address - 1
1547 if not self.reader.IsValidAddress(address): return None
1548 object = Map(self, None, address)
1549 return object
1550
1551 def IntSize(self):
1552 return 4
1553
1554 def PointerSize(self):
1555 return self.reader.PointerSize()
1556
1557 def ObjectAlignmentMask(self):
1558 return self.PointerSize() - 1
1559
1560 def MapAlignmentMask(self):
1561 if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64:
1562 return (1 << 4) - 1
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001563 elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM:
1564 return (1 << 4) - 1
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001565 elif self.reader.arch == MD_CPU_ARCHITECTURE_X86:
1566 return (1 << 5) - 1
1567
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001568 def PageAlignmentMask(self):
1569 return (1 << 20) - 1
1570
1571
1572class KnownObject(HeapObject):
1573 def __init__(self, heap, known_name):
1574 HeapObject.__init__(self, heap, None, None)
1575 self.known_name = known_name
1576
1577 def __str__(self):
1578 return "<%s>" % self.known_name
1579
1580
1581class KnownMap(HeapObject):
1582 def __init__(self, heap, known_name, instance_type):
1583 HeapObject.__init__(self, heap, None, None)
1584 self.instance_type = instance_type
1585 self.known_name = known_name
1586
1587 def __str__(self):
1588 return "<%s>" % self.known_name
1589
1590
1591COMMENT_RE = re.compile(r"^C (0x[0-9a-fA-F]+) (.*)$")
1592PAGEADDRESS_RE = re.compile(
1593 r"^P (mappage|pointerpage|datapage) (0x[0-9a-fA-F]+)$")
1594
1595
1596class InspectionInfo(object):
1597 def __init__(self, minidump_name, reader):
1598 self.comment_file = minidump_name + ".comments"
1599 self.address_comments = {}
1600 self.page_address = {}
1601 if os.path.exists(self.comment_file):
1602 with open(self.comment_file, "r") as f:
1603 lines = f.readlines()
1604 f.close()
1605
1606 for l in lines:
1607 m = COMMENT_RE.match(l)
1608 if m:
1609 self.address_comments[int(m.group(1), 0)] = m.group(2)
1610 m = PAGEADDRESS_RE.match(l)
1611 if m:
1612 self.page_address[m.group(1)] = int(m.group(2), 0)
1613 self.reader = reader
1614 self.styles = {}
1615 self.color_addresses()
1616 return
1617
1618 def get_page_address(self, page_kind):
1619 return self.page_address.get(page_kind, 0)
1620
1621 def save_page_address(self, page_kind, address):
1622 with open(self.comment_file, "a") as f:
1623 f.write("P %s 0x%x\n" % (page_kind, address))
1624 f.close()
1625
1626 def color_addresses(self):
1627 # Color all stack addresses.
1628 exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
1629 stack_top = self.reader.ExceptionSP()
1630 stack_bottom = exception_thread.stack.start + \
1631 exception_thread.stack.memory.data_size
1632 frame_pointer = self.reader.ExceptionFP()
1633 self.styles[frame_pointer] = "frame"
1634 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
1635 self.styles[slot] = "stackaddress"
1636 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
1637 maybe_address = self.reader.ReadUIntPtr(slot)
1638 self.styles[maybe_address] = "stackval"
1639 if slot == frame_pointer:
1640 self.styles[slot] = "frame"
1641 frame_pointer = maybe_address
1642 self.styles[self.reader.ExceptionIP()] = "pc"
1643
1644 def get_style_class(self, address):
1645 return self.styles.get(address, None)
1646
1647 def get_style_class_string(self, address):
1648 style = self.get_style_class(address)
1649 if style != None:
1650 return " class=\"%s\" " % style
1651 else:
1652 return ""
1653
1654 def set_comment(self, address, comment):
1655 self.address_comments[address] = comment
1656 with open(self.comment_file, "a") as f:
1657 f.write("C 0x%x %s\n" % (address, comment))
1658 f.close()
1659
1660 def get_comment(self, address):
1661 return self.address_comments.get(address, "")
1662
1663
1664class InspectionPadawan(object):
1665 """The padawan can improve annotations by sensing well-known objects."""
1666 def __init__(self, reader, heap):
1667 self.reader = reader
1668 self.heap = heap
1669 self.known_first_map_page = 0
1670 self.known_first_data_page = 0
1671 self.known_first_pointer_page = 0
1672
1673 def __getattr__(self, name):
1674 """An InspectionPadawan can be used instead of V8Heap, even though
1675 it does not inherit from V8Heap (aka. mixin)."""
1676 return getattr(self.heap, name)
1677
1678 def GetPageOffset(self, tagged_address):
1679 return tagged_address & self.heap.PageAlignmentMask()
1680
1681 def IsInKnownMapSpace(self, tagged_address):
1682 page_address = tagged_address & ~self.heap.PageAlignmentMask()
1683 return page_address == self.known_first_map_page
1684
1685 def IsInKnownOldSpace(self, tagged_address):
1686 page_address = tagged_address & ~self.heap.PageAlignmentMask()
1687 return page_address in [self.known_first_data_page,
1688 self.known_first_pointer_page]
1689
1690 def ContainingKnownOldSpaceName(self, tagged_address):
1691 page_address = tagged_address & ~self.heap.PageAlignmentMask()
1692 if page_address == self.known_first_data_page: return "OLD_DATA_SPACE"
1693 if page_address == self.known_first_pointer_page: return "OLD_POINTER_SPACE"
1694 return None
1695
1696 def SenseObject(self, tagged_address):
1697 if self.IsInKnownOldSpace(tagged_address):
1698 offset = self.GetPageOffset(tagged_address)
1699 lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset)
1700 known_obj_name = KNOWN_OBJECTS.get(lookup_key)
1701 if known_obj_name:
1702 return KnownObject(self, known_obj_name)
1703 if self.IsInKnownMapSpace(tagged_address):
1704 known_map = self.SenseMap(tagged_address)
1705 if known_map:
1706 return known_map
1707 found_obj = self.heap.FindObject(tagged_address)
1708 if found_obj: return found_obj
1709 address = tagged_address - 1
1710 if self.reader.IsValidAddress(address):
1711 map_tagged_address = self.reader.ReadUIntPtr(address)
1712 map = self.SenseMap(map_tagged_address)
1713 if map is None: return None
1714 instance_type_name = INSTANCE_TYPES.get(map.instance_type)
1715 if instance_type_name is None: return None
1716 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
1717 return cls(self, map, address)
1718 return None
1719
1720 def SenseMap(self, tagged_address):
1721 if self.IsInKnownMapSpace(tagged_address):
1722 offset = self.GetPageOffset(tagged_address)
1723 known_map_info = KNOWN_MAPS.get(offset)
1724 if known_map_info:
1725 known_map_type, known_map_name = known_map_info
1726 return KnownMap(self, known_map_name, known_map_type)
1727 found_map = self.heap.FindMap(tagged_address)
1728 if found_map: return found_map
1729 return None
1730
1731 def FindObjectOrSmi(self, tagged_address):
1732 """When used as a mixin in place of V8Heap."""
1733 found_obj = self.SenseObject(tagged_address)
1734 if found_obj: return found_obj
1735 if (tagged_address & 1) == 0:
1736 return "Smi(%d)" % (tagged_address / 2)
1737 else:
1738 return "Unknown(%s)" % self.reader.FormatIntPtr(tagged_address)
1739
1740 def FindObject(self, tagged_address):
1741 """When used as a mixin in place of V8Heap."""
1742 raise NotImplementedError
1743
1744 def FindMap(self, tagged_address):
1745 """When used as a mixin in place of V8Heap."""
1746 raise NotImplementedError
1747
1748 def PrintKnowledge(self):
1749 print " known_first_map_page = %s\n"\
1750 " known_first_data_page = %s\n"\
1751 " known_first_pointer_page = %s" % (
1752 self.reader.FormatIntPtr(self.known_first_map_page),
1753 self.reader.FormatIntPtr(self.known_first_data_page),
1754 self.reader.FormatIntPtr(self.known_first_pointer_page))
1755
1756WEB_HEADER = """
1757<!DOCTYPE html>
1758<html>
1759<head>
1760<meta content="text/html; charset=utf-8" http-equiv="content-type">
1761<style media="screen" type="text/css">
1762
1763.code {
1764 font-family: monospace;
1765}
1766
1767.dmptable {
1768 border-collapse : collapse;
1769 border-spacing : 0px;
1770}
1771
1772.codedump {
1773 border-collapse : collapse;
1774 border-spacing : 0px;
1775}
1776
1777.addrcomments {
1778 border : 0px;
1779}
1780
1781.register {
1782 padding-right : 1em;
1783}
1784
1785.header {
1786 clear : both;
1787}
1788
1789.header .navigation {
1790 float : left;
1791}
1792
1793.header .dumpname {
1794 float : right;
1795}
1796
1797tr.highlight-line {
1798 background-color : yellow;
1799}
1800
1801.highlight {
1802 background-color : magenta;
1803}
1804
1805tr.inexact-highlight-line {
1806 background-color : pink;
1807}
1808
1809input {
1810 background-color: inherit;
1811 border: 1px solid LightGray;
1812}
1813
1814.dumpcomments {
1815 border : 1px solid LightGray;
1816 width : 32em;
1817}
1818
1819.regions td {
1820 padding:0 15px 0 15px;
1821}
1822
1823.stackframe td {
1824 background-color : cyan;
1825}
1826
1827.stackaddress {
1828 background-color : LightGray;
1829}
1830
1831.stackval {
1832 background-color : LightCyan;
1833}
1834
1835.frame {
1836 background-color : cyan;
1837}
1838
1839.commentinput {
1840 width : 20em;
1841}
1842
1843a.nodump:visited {
1844 color : black;
1845 text-decoration : none;
1846}
1847
1848a.nodump:link {
1849 color : black;
1850 text-decoration : none;
1851}
1852
1853a:visited {
1854 color : blueviolet;
1855}
1856
1857a:link {
1858 color : blue;
1859}
1860
1861.disasmcomment {
1862 color : DarkGreen;
1863}
1864
1865</style>
1866
1867<script type="application/javascript">
1868
1869var address_str = "address-";
1870var address_len = address_str.length;
1871
1872function comment() {
1873 var s = event.srcElement.id;
1874 var index = s.indexOf(address_str);
1875 if (index >= 0) {
1876 send_comment(s.substring(index + address_len), event.srcElement.value);
1877 }
1878}
1879
1880function send_comment(address, comment) {
1881 xmlhttp = new XMLHttpRequest();
1882 address = encodeURIComponent(address)
1883 comment = encodeURIComponent(comment)
1884 xmlhttp.open("GET",
1885 "setcomment?%(query_dump)s&address=" + address +
1886 "&comment=" + comment, true);
1887 xmlhttp.send();
1888}
1889
1890var dump_str = "dump-";
1891var dump_len = dump_str.length;
1892
1893function dump_comment() {
1894 var s = event.srcElement.id;
1895 var index = s.indexOf(dump_str);
1896 if (index >= 0) {
1897 send_dump_desc(s.substring(index + dump_len), event.srcElement.value);
1898 }
1899}
1900
1901function send_dump_desc(name, desc) {
1902 xmlhttp = new XMLHttpRequest();
1903 name = encodeURIComponent(name)
1904 desc = encodeURIComponent(desc)
1905 xmlhttp.open("GET",
1906 "setdumpdesc?dump=" + name +
1907 "&description=" + desc, true);
1908 xmlhttp.send();
1909}
1910
1911function onpage(kind, address) {
1912 xmlhttp = new XMLHttpRequest();
1913 kind = encodeURIComponent(kind)
1914 address = encodeURIComponent(address)
1915 xmlhttp.onreadystatechange = function() {
1916 if (xmlhttp.readyState==4 && xmlhttp.status==200) {
1917 location.reload(true)
1918 }
1919 };
1920 xmlhttp.open("GET",
1921 "setpageaddress?%(query_dump)s&kind=" + kind +
1922 "&address=" + address);
1923 xmlhttp.send();
1924}
1925
1926</script>
1927
1928<title>Dump %(dump_name)s</title>
1929</head>
1930
1931<body>
1932 <div class="header">
1933 <form class="navigation" action="search.html">
1934 <a href="summary.html?%(query_dump)s">Context info</a>&nbsp;&nbsp;&nbsp;
1935 <a href="info.html?%(query_dump)s">Dump info</a>&nbsp;&nbsp;&nbsp;
1936 <a href="modules.html?%(query_dump)s">Modules</a>&nbsp;&nbsp;&nbsp;
1937 &nbsp;
1938 <input type="search" name="val">
1939 <input type="submit" name="search" value="Search">
1940 <input type="hidden" name="dump" value="%(dump_name)s">
1941 </form>
1942 <form class="navigation" action="disasm.html#highlight">
1943 &nbsp;
1944 &nbsp;
1945 &nbsp;
1946 <input type="search" name="val">
1947 <input type="submit" name="disasm" value="Disasm">
1948 &nbsp;
1949 &nbsp;
1950 &nbsp;
1951 <a href="dumps.html">Dumps...</a>
1952 </form>
1953 </div>
1954 <br>
1955 <hr>
1956"""
1957
1958
1959WEB_FOOTER = """
1960</body>
1961</html>
1962"""
1963
1964
1965class WebParameterError(Exception):
1966 def __init__(self, message):
1967 Exception.__init__(self, message)
1968
1969
1970class InspectionWebHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1971 def formatter(self, query_components):
1972 name = query_components.get("dump", [None])[0]
1973 return self.server.get_dump_formatter(name)
1974
1975 def send_success_html_headers(self):
1976 self.send_response(200)
1977 self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
1978 self.send_header("Pragma", "no-cache")
1979 self.send_header("Expires", "0")
1980 self.send_header('Content-type','text/html')
1981 self.end_headers()
1982 return
1983
1984 def do_GET(self):
1985 try:
1986 parsedurl = urlparse.urlparse(self.path)
1987 query_components = urlparse.parse_qs(parsedurl.query)
1988 if parsedurl.path == "/dumps.html":
1989 self.send_success_html_headers()
1990 self.server.output_dumps(self.wfile)
1991 elif parsedurl.path == "/summary.html":
1992 self.send_success_html_headers()
1993 self.formatter(query_components).output_summary(self.wfile)
1994 elif parsedurl.path == "/info.html":
1995 self.send_success_html_headers()
1996 self.formatter(query_components).output_info(self.wfile)
1997 elif parsedurl.path == "/modules.html":
1998 self.send_success_html_headers()
1999 self.formatter(query_components).output_modules(self.wfile)
2000 elif parsedurl.path == "/search.html":
2001 address = query_components.get("val", [])
2002 if len(address) != 1:
2003 self.send_error(404, "Invalid params")
2004 return
2005 self.send_success_html_headers()
2006 self.formatter(query_components).output_search_res(
2007 self.wfile, address[0])
2008 elif parsedurl.path == "/disasm.html":
2009 address = query_components.get("val", [])
2010 exact = query_components.get("exact", ["on"])
2011 if len(address) != 1:
2012 self.send_error(404, "Invalid params")
2013 return
2014 self.send_success_html_headers()
2015 self.formatter(query_components).output_disasm(
2016 self.wfile, address[0], exact[0])
2017 elif parsedurl.path == "/data.html":
2018 address = query_components.get("val", [])
2019 datakind = query_components.get("type", ["address"])
2020 if len(address) == 1 and len(datakind) == 1:
2021 self.send_success_html_headers()
2022 self.formatter(query_components).output_data(
2023 self.wfile, address[0], datakind[0])
2024 else:
2025 self.send_error(404,'Invalid params')
2026 elif parsedurl.path == "/setdumpdesc":
2027 name = query_components.get("dump", [""])
2028 description = query_components.get("description", [""])
2029 if len(name) == 1 and len(description) == 1:
2030 name = name[0]
2031 description = description[0]
2032 if self.server.set_dump_desc(name, description):
2033 self.send_success_html_headers()
2034 self.wfile.write("OK")
2035 return
2036 self.send_error(404,'Invalid params')
2037 elif parsedurl.path == "/setcomment":
2038 address = query_components.get("address", [])
2039 comment = query_components.get("comment", [""])
2040 if len(address) == 1 and len(comment) == 1:
2041 address = address[0]
2042 comment = comment[0]
2043 self.formatter(query_components).set_comment(address, comment)
2044 self.send_success_html_headers()
2045 self.wfile.write("OK")
2046 else:
2047 self.send_error(404,'Invalid params')
2048 elif parsedurl.path == "/setpageaddress":
2049 kind = query_components.get("kind", [])
2050 address = query_components.get("address", [""])
2051 if len(kind) == 1 and len(address) == 1:
2052 kind = kind[0]
2053 address = address[0]
2054 self.formatter(query_components).set_page_address(kind, address)
2055 self.send_success_html_headers()
2056 self.wfile.write("OK")
2057 else:
2058 self.send_error(404,'Invalid params')
2059 else:
2060 self.send_error(404,'File Not Found: %s' % self.path)
2061
2062 except IOError:
2063 self.send_error(404,'File Not Found: %s' % self.path)
2064
2065 except WebParameterError as e:
2066 self.send_error(404, 'Web parameter error: %s' % e.message)
2067
2068
2069HTML_REG_FORMAT = "<span class=\"register\"><b>%s</b>:&nbsp;%s</span>\n"
2070
2071
2072class InspectionWebFormatter(object):
2073 CONTEXT_FULL = 0
2074 CONTEXT_SHORT = 1
2075
2076 def __init__(self, switches, minidump_name, http_server):
2077 self.dumpfilename = os.path.split(minidump_name)[1]
2078 self.encfilename = urllib.urlencode({ 'dump' : self.dumpfilename })
2079 self.reader = MinidumpReader(switches, minidump_name)
2080 self.server = http_server
2081
2082 # Set up the heap
2083 exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
2084 stack_top = self.reader.ExceptionSP()
2085 stack_bottom = exception_thread.stack.start + \
2086 exception_thread.stack.memory.data_size
2087 stack_map = {self.reader.ExceptionIP(): -1}
2088 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
2089 maybe_address = self.reader.ReadUIntPtr(slot)
2090 if not maybe_address in stack_map:
2091 stack_map[maybe_address] = slot
2092 self.heap = V8Heap(self.reader, stack_map)
2093
2094 self.padawan = InspectionPadawan(self.reader, self.heap)
2095 self.comments = InspectionInfo(minidump_name, self.reader)
2096 self.padawan.known_first_data_page = (
2097 self.comments.get_page_address("datapage"))
2098 self.padawan.known_first_map_page = (
2099 self.comments.get_page_address("mappage"))
2100 self.padawan.known_first_pointer_page = (
2101 self.comments.get_page_address("pointerpage"))
2102
2103 def set_comment(self, straddress, comment):
2104 try:
2105 address = int(straddress, 0)
2106 self.comments.set_comment(address, comment)
2107 except ValueError:
2108 print "Invalid address"
2109
2110 def set_page_address(self, kind, straddress):
2111 try:
2112 address = int(straddress, 0)
2113 if kind == "datapage":
2114 self.padawan.known_first_data_page = address
2115 elif kind == "mappage":
2116 self.padawan.known_first_map_page = address
2117 elif kind == "pointerpage":
2118 self.padawan.known_first_pointer_page = address
2119 self.comments.save_page_address(kind, address)
2120 except ValueError:
2121 print "Invalid address"
2122
2123 def td_from_address(self, f, address):
2124 f.write("<td %s>" % self.comments.get_style_class_string(address))
2125
2126 def format_address(self, maybeaddress, straddress = None):
2127 if maybeaddress is None:
2128 return "not in dump"
2129 else:
2130 if straddress is None:
2131 straddress = "0x" + self.reader.FormatIntPtr(maybeaddress)
2132 style_class = ""
2133 if not self.reader.IsValidAddress(maybeaddress):
2134 style_class = " class=\"nodump\""
2135 return ("<a %s href=\"search.html?%s&amp;val=%s\">%s</a>" %
2136 (style_class, self.encfilename, straddress, straddress))
2137
2138 def output_header(self, f):
2139 f.write(WEB_HEADER %
2140 { "query_dump" : self.encfilename,
2141 "dump_name" : cgi.escape(self.dumpfilename) })
2142
2143 def output_footer(self, f):
2144 f.write(WEB_FOOTER)
2145
2146 MAX_CONTEXT_STACK = 4096
2147
2148 def output_summary(self, f):
2149 self.output_header(f)
2150 f.write('<div class="code">')
2151 self.output_context(f, InspectionWebFormatter.CONTEXT_SHORT)
2152 self.output_disasm_pc(f)
2153
2154 # Output stack
2155 exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
2156 stack_bottom = exception_thread.stack.start + \
2157 min(exception_thread.stack.memory.data_size, self.MAX_CONTEXT_STACK)
2158 stack_top = self.reader.ExceptionSP()
2159 self.output_words(f, stack_top - 16, stack_bottom, stack_top, "Stack")
2160
2161 f.write('</div>')
2162 self.output_footer(f)
2163 return
2164
2165 def output_info(self, f):
2166 self.output_header(f)
2167 f.write("<h3>Dump info</h3>\n")
2168 f.write("Description: ")
2169 self.server.output_dump_desc_field(f, self.dumpfilename)
2170 f.write("<br>\n")
2171 f.write("Filename: ")
2172 f.write("<span class=\"code\">%s</span><br>\n" % (self.dumpfilename))
2173 dt = datetime.datetime.fromtimestamp(self.reader.header.time_date_stampt)
2174 f.write("Timestamp: %s<br>\n" % dt.strftime('%Y-%m-%d %H:%M:%S'))
2175 self.output_context(f, InspectionWebFormatter.CONTEXT_FULL)
2176 self.output_address_ranges(f)
2177 self.output_footer(f)
2178 return
2179
2180 def output_address_ranges(self, f):
2181 regions = {}
2182 def print_region(_reader, start, size, _location):
2183 regions[start] = size
2184 self.reader.ForEachMemoryRegion(print_region)
2185 f.write("<h3>Available memory regions</h3>\n")
2186 f.write('<div class="code">')
2187 f.write("<table class=\"regions\">\n")
2188 f.write("<thead><tr>")
2189 f.write("<th>Start address</th>")
2190 f.write("<th>End address</th>")
2191 f.write("<th>Number of bytes</th>")
2192 f.write("</tr></thead>\n")
2193 for start in sorted(regions):
2194 size = regions[start]
2195 f.write("<tr>")
2196 f.write("<td>%s</td>" % self.format_address(start))
2197 f.write("<td>&nbsp;%s</td>" % self.format_address(start + size))
2198 f.write("<td>&nbsp;%d</td>" % size)
2199 f.write("</tr>\n")
2200 f.write("</table>\n")
2201 f.write('</div>')
2202 return
2203
2204 def output_module_details(self, f, module):
2205 f.write("<b>%s</b>" % GetModuleName(self.reader, module))
2206 file_version = GetVersionString(module.version_info.dwFileVersionMS,
2207 module.version_info.dwFileVersionLS)
2208 product_version = GetVersionString(module.version_info.dwProductVersionMS,
2209 module.version_info.dwProductVersionLS)
2210 f.write("<br>&nbsp;&nbsp;\n")
2211 f.write("base: %s" % self.reader.FormatIntPtr(module.base_of_image))
2212 f.write("<br>&nbsp;&nbsp;\n")
2213 f.write(" end: %s" % self.reader.FormatIntPtr(module.base_of_image +
2214 module.size_of_image))
2215 f.write("<br>&nbsp;&nbsp;\n")
2216 f.write(" file version: %s" % file_version)
2217 f.write("<br>&nbsp;&nbsp;\n")
2218 f.write(" product version: %s" % product_version)
2219 f.write("<br>&nbsp;&nbsp;\n")
2220 time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
2221 f.write(" timestamp: %s" % time_date_stamp)
2222 f.write("<br>\n");
2223
2224 def output_modules(self, f):
2225 self.output_header(f)
2226 f.write('<div class="code">')
2227 for module in self.reader.module_list.modules:
2228 self.output_module_details(f, module)
2229 f.write("</div>")
2230 self.output_footer(f)
2231 return
2232
2233 def output_context(self, f, details):
2234 exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
2235 f.write("<h3>Exception context</h3>")
2236 f.write('<div class="code">\n')
2237 f.write("Thread id: %d" % exception_thread.id)
2238 f.write("&nbsp;&nbsp; Exception code: %08X\n" %
2239 self.reader.exception.exception.code)
2240 if details == InspectionWebFormatter.CONTEXT_FULL:
2241 if self.reader.exception.exception.parameter_count > 0:
2242 f.write("&nbsp;&nbsp; Exception parameters: \n")
2243 for i in xrange(0, self.reader.exception.exception.parameter_count):
2244 f.write("%08x" % self.reader.exception.exception.information[i])
2245 f.write("<br><br>\n")
2246
2247 for r in CONTEXT_FOR_ARCH[self.reader.arch]:
2248 f.write(HTML_REG_FORMAT %
2249 (r, self.format_address(self.reader.Register(r))))
2250 # TODO(vitalyr): decode eflags.
2251 if self.reader.arch == MD_CPU_ARCHITECTURE_ARM:
2252 f.write("<b>cpsr</b>: %s" % bin(self.reader.exception_context.cpsr)[2:])
2253 else:
2254 f.write("<b>eflags</b>: %s" %
2255 bin(self.reader.exception_context.eflags)[2:])
2256 f.write('</div>\n')
2257 return
2258
2259 def align_down(self, a, size):
2260 alignment_correction = a % size
2261 return a - alignment_correction
2262
2263 def align_up(self, a, size):
2264 alignment_correction = (size - 1) - ((a + size - 1) % size)
2265 return a + alignment_correction
2266
2267 def format_object(self, address):
2268 heap_object = self.padawan.SenseObject(address)
2269 return cgi.escape(str(heap_object or ""))
2270
2271 def output_data(self, f, straddress, datakind):
2272 try:
2273 self.output_header(f)
2274 address = int(straddress, 0)
2275 if not self.reader.IsValidAddress(address):
2276 f.write("<h3>Address 0x%x not found in the dump.</h3>" % address)
2277 return
2278 region = self.reader.FindRegion(address)
2279 if datakind == "address":
2280 self.output_words(f, region[0], region[0] + region[1], address, "Dump")
2281 elif datakind == "ascii":
2282 self.output_ascii(f, region[0], region[0] + region[1], address)
2283 self.output_footer(f)
2284
2285 except ValueError:
2286 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
2287 return
2288
2289 def output_words(self, f, start_address, end_address,
2290 highlight_address, desc):
2291 region = self.reader.FindRegion(highlight_address)
2292 if region is None:
2293 f.write("<h3>Address 0x%x not found in the dump.</h3>\n" %
2294 (highlight_address))
2295 return
2296 size = self.heap.PointerSize()
2297 start_address = self.align_down(start_address, size)
2298 low = self.align_down(region[0], size)
2299 high = self.align_up(region[0] + region[1], size)
2300 if start_address < low:
2301 start_address = low
2302 end_address = self.align_up(end_address, size)
2303 if end_address > high:
2304 end_address = high
2305
2306 expand = ""
2307 if start_address != low or end_address != high:
2308 expand = ("(<a href=\"data.html?%s&amp;val=0x%x#highlight\">"
2309 " more..."
2310 " </a>)" %
2311 (self.encfilename, highlight_address))
2312
2313 f.write("<h3>%s 0x%x - 0x%x, "
2314 "highlighting <a href=\"#highlight\">0x%x</a> %s</h3>\n" %
2315 (desc, start_address, end_address, highlight_address, expand))
2316 f.write('<div class="code">')
2317 f.write("<table class=\"codedump\">\n")
2318
2319 for slot in xrange(start_address, end_address, size):
2320 heap_object = ""
2321 maybe_address = None
2322 end_region = region[0] + region[1]
2323 if slot < region[0] or slot + size > end_region:
2324 straddress = "0x"
2325 for i in xrange(end_region, slot + size):
2326 straddress += "??"
2327 for i in reversed(
2328 xrange(max(slot, region[0]), min(slot + size, end_region))):
2329 straddress += "%02x" % self.reader.ReadU8(i)
2330 for i in xrange(slot, region[0]):
2331 straddress += "??"
2332 else:
2333 maybe_address = self.reader.ReadUIntPtr(slot)
2334 straddress = self.format_address(maybe_address)
2335 if maybe_address:
2336 heap_object = self.format_object(maybe_address)
2337
2338 address_fmt = "%s&nbsp;</td>\n"
2339 if slot == highlight_address:
2340 f.write("<tr class=\"highlight-line\">\n")
2341 address_fmt = "<a id=\"highlight\"></a>%s&nbsp;</td>\n"
2342 elif slot < highlight_address and highlight_address < slot + size:
2343 f.write("<tr class=\"inexact-highlight-line\">\n")
2344 address_fmt = "<a id=\"highlight\"></a>%s&nbsp;</td>\n"
2345 else:
2346 f.write("<tr>\n")
2347
2348 f.write(" <td>")
2349 self.output_comment_box(f, "da-", slot)
2350 f.write("</td>\n")
2351 f.write(" ")
2352 self.td_from_address(f, slot)
2353 f.write(address_fmt % self.format_address(slot))
2354 f.write(" ")
2355 self.td_from_address(f, maybe_address)
2356 f.write(":&nbsp; %s &nbsp;</td>\n" % straddress)
2357 f.write(" <td>")
2358 if maybe_address != None:
2359 self.output_comment_box(
2360 f, "sv-" + self.reader.FormatIntPtr(slot), maybe_address)
2361 f.write(" </td>\n")
2362 f.write(" <td>%s</td>\n" % (heap_object or ''))
2363 f.write("</tr>\n")
2364 f.write("</table>\n")
2365 f.write("</div>")
2366 return
2367
2368 def output_ascii(self, f, start_address, end_address, highlight_address):
2369 region = self.reader.FindRegion(highlight_address)
2370 if region is None:
2371 f.write("<h3>Address %x not found in the dump.</h3>" %
2372 highlight_address)
2373 return
2374 if start_address < region[0]:
2375 start_address = region[0]
2376 if end_address > region[0] + region[1]:
2377 end_address = region[0] + region[1]
2378
2379 expand = ""
2380 if start_address != region[0] or end_address != region[0] + region[1]:
2381 link = ("data.html?%s&amp;val=0x%x&amp;type=ascii#highlight" %
2382 (self.encfilename, highlight_address))
2383 expand = "(<a href=\"%s\">more...</a>)" % link
2384
2385 f.write("<h3>ASCII dump 0x%x - 0x%x, highlighting 0x%x %s</h3>" %
2386 (start_address, end_address, highlight_address, expand))
2387
2388 line_width = 64
2389
2390 f.write('<div class="code">')
2391
2392 start = self.align_down(start_address, line_width)
2393
2394 for address in xrange(start, end_address):
2395 if address % 64 == 0:
2396 if address != start:
2397 f.write("<br>")
2398 f.write("0x%08x:&nbsp;" % address)
2399 if address < start_address:
2400 f.write("&nbsp;")
2401 else:
2402 if address == highlight_address:
2403 f.write("<span class=\"highlight\">")
2404 code = self.reader.ReadU8(address)
2405 if code < 127 and code >= 32:
2406 f.write("&#")
2407 f.write(str(code))
2408 f.write(";")
2409 else:
2410 f.write("&middot;")
2411 if address == highlight_address:
2412 f.write("</span>")
2413 f.write("</div>")
2414 return
2415
2416 def output_disasm(self, f, straddress, strexact):
2417 try:
2418 self.output_header(f)
2419 address = int(straddress, 0)
2420 if not self.reader.IsValidAddress(address):
2421 f.write("<h3>Address 0x%x not found in the dump.</h3>" % address)
2422 return
2423 region = self.reader.FindRegion(address)
2424 self.output_disasm_range(
2425 f, region[0], region[0] + region[1], address, strexact == "on")
2426 self.output_footer(f)
2427 except ValueError:
2428 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
2429 return
2430
2431 def output_disasm_range(
2432 self, f, start_address, end_address, highlight_address, exact):
2433 region = self.reader.FindRegion(highlight_address)
2434 if start_address < region[0]:
2435 start_address = region[0]
2436 if end_address > region[0] + region[1]:
2437 end_address = region[0] + region[1]
2438 count = end_address - start_address
2439 lines = self.reader.GetDisasmLines(start_address, count)
2440 found = False
2441 if exact:
2442 for line in lines:
2443 if line[0] + start_address == highlight_address:
2444 found = True
2445 break
2446 if not found:
2447 start_address = highlight_address
2448 count = end_address - start_address
2449 lines = self.reader.GetDisasmLines(highlight_address, count)
2450 expand = ""
2451 if start_address != region[0] or end_address != region[0] + region[1]:
2452 exactness = ""
2453 if exact and not found and end_address == region[0] + region[1]:
2454 exactness = "&amp;exact=off"
2455 expand = ("(<a href=\"disasm.html?%s%s"
2456 "&amp;val=0x%x#highlight\">more...</a>)" %
2457 (self.encfilename, exactness, highlight_address))
2458
2459 f.write("<h3>Disassembling 0x%x - 0x%x, highlighting 0x%x %s</h3>" %
2460 (start_address, end_address, highlight_address, expand))
2461 f.write('<div class="code">')
2462 f.write("<table class=\"codedump\">\n");
2463 for i in xrange(0, len(lines)):
2464 line = lines[i]
2465 next_address = count
2466 if i + 1 < len(lines):
2467 next_line = lines[i + 1]
2468 next_address = next_line[0]
2469 self.format_disasm_line(
2470 f, start_address, line, next_address, highlight_address)
2471 f.write("</table>\n")
2472 f.write("</div>")
2473 return
2474
2475 def annotate_disasm_addresses(self, line):
2476 extra = []
2477 for m in ADDRESS_RE.finditer(line):
2478 maybe_address = int(m.group(0), 16)
2479 formatted_address = self.format_address(maybe_address, m.group(0))
2480 line = line.replace(m.group(0), formatted_address)
2481 object_info = self.padawan.SenseObject(maybe_address)
2482 if not object_info:
2483 continue
2484 extra.append(cgi.escape(str(object_info)))
2485 if len(extra) == 0:
2486 return line
2487 return ("%s <span class=\"disasmcomment\">;; %s</span>" %
2488 (line, ", ".join(extra)))
2489
2490 def format_disasm_line(
2491 self, f, start, line, next_address, highlight_address):
2492 line_address = start + line[0]
2493 address_fmt = " <td>%s</td>\n"
2494 if line_address == highlight_address:
2495 f.write("<tr class=\"highlight-line\">\n")
2496 address_fmt = " <td><a id=\"highlight\">%s</a></td>\n"
2497 elif (line_address < highlight_address and
2498 highlight_address < next_address + start):
2499 f.write("<tr class=\"inexact-highlight-line\">\n")
2500 address_fmt = " <td><a id=\"highlight\">%s</a></td>\n"
2501 else:
2502 f.write("<tr>\n")
2503 num_bytes = next_address - line[0]
2504 stack_slot = self.heap.stack_map.get(line_address)
2505 marker = ""
2506 if stack_slot:
2507 marker = "=>"
2508 op_offset = 3 * num_bytes - 1
2509
2510 code = line[1]
2511 # Compute the actual call target which the disassembler is too stupid
2512 # to figure out (it adds the call offset to the disassembly offset rather
2513 # than the absolute instruction address).
2514 if self.heap.reader.arch == MD_CPU_ARCHITECTURE_X86:
2515 if code.startswith("e8"):
2516 words = code.split()
2517 if len(words) > 6 and words[5] == "call":
2518 offset = int(words[4] + words[3] + words[2] + words[1], 16)
2519 target = (line_address + offset + 5) & 0xFFFFFFFF
2520 code = code.replace(words[6], "0x%08x" % target)
2521 # TODO(jkummerow): port this hack to ARM and x64.
2522
2523 opcodes = code[:op_offset]
2524 code = self.annotate_disasm_addresses(code[op_offset:])
2525 f.write(" <td>")
2526 self.output_comment_box(f, "codel-", line_address)
2527 f.write("</td>\n")
2528 f.write(address_fmt % marker)
2529 f.write(" ")
2530 self.td_from_address(f, line_address)
2531 f.write("%s (+0x%x)</td>\n" %
2532 (self.format_address(line_address), line[0]))
2533 f.write(" <td>:&nbsp;%s&nbsp;</td>\n" % opcodes)
2534 f.write(" <td>%s</td>\n" % code)
2535 f.write("</tr>\n")
2536
2537 def output_comment_box(self, f, prefix, address):
2538 f.write("<input type=\"text\" class=\"commentinput\" "
2539 "id=\"%s-address-0x%s\" onchange=\"comment()\" value=\"%s\">" %
2540 (prefix,
2541 self.reader.FormatIntPtr(address),
2542 cgi.escape(self.comments.get_comment(address)) or ""))
2543
2544 MAX_FOUND_RESULTS = 100
2545
2546 def output_find_results(self, f, results):
2547 f.write("Addresses")
2548 toomany = len(results) > self.MAX_FOUND_RESULTS
2549 if toomany:
2550 f.write("(found %i results, displaying only first %i)" %
2551 (len(results), self.MAX_FOUND_RESULTS))
2552 f.write(": \n")
2553 results = sorted(results)
2554 results = results[:min(len(results), self.MAX_FOUND_RESULTS)]
2555 for address in results:
2556 f.write("<span %s>%s</span>\n" %
2557 (self.comments.get_style_class_string(address),
2558 self.format_address(address)))
2559 if toomany:
2560 f.write("...\n")
2561
2562
2563 def output_page_info(self, f, page_kind, page_address, my_page_address):
2564 if my_page_address == page_address and page_address != 0:
2565 f.write("Marked first %s page.\n" % page_kind)
2566 else:
2567 f.write("<span id=\"%spage\" style=\"display:none\">" % page_kind)
2568 f.write("Marked first %s page." % page_kind)
2569 f.write("</span>\n")
2570 f.write("<button onclick=\"onpage('%spage', '0x%x')\">" %
2571 (page_kind, my_page_address))
2572 f.write("Mark as first %s page</button>\n" % page_kind)
2573 return
2574
2575 def output_search_res(self, f, straddress):
2576 try:
2577 self.output_header(f)
2578 f.write("<h3>Search results for %s</h3>" % straddress)
2579
2580 address = int(straddress, 0)
2581
2582 f.write("Comment: ")
2583 self.output_comment_box(f, "search-", address)
2584 f.write("<br>\n")
2585
2586 page_address = address & ~self.heap.PageAlignmentMask()
2587
2588 f.write("Page info: \n")
2589 self.output_page_info(f, "data", self.padawan.known_first_data_page, \
2590 page_address)
2591 self.output_page_info(f, "map", self.padawan.known_first_map_page, \
2592 page_address)
2593 self.output_page_info(f, "pointer", \
2594 self.padawan.known_first_pointer_page, \
2595 page_address)
2596
2597 if not self.reader.IsValidAddress(address):
2598 f.write("<h3>The contents at address %s not found in the dump.</h3>" % \
2599 straddress)
2600 else:
2601 # Print as words
2602 self.output_words(f, address - 8, address + 32, address, "Dump")
2603
2604 # Print as ASCII
2605 f.write("<hr>\n")
2606 self.output_ascii(f, address, address + 256, address)
2607
2608 # Print as code
2609 f.write("<hr>\n")
2610 self.output_disasm_range(f, address - 16, address + 16, address, True)
2611
2612 aligned_res, unaligned_res = self.reader.FindWordList(address)
2613
2614 if len(aligned_res) > 0:
2615 f.write("<h3>Occurrences of 0x%x at aligned addresses</h3>\n" %
2616 address)
2617 self.output_find_results(f, aligned_res)
2618
2619 if len(unaligned_res) > 0:
2620 f.write("<h3>Occurrences of 0x%x at unaligned addresses</h3>\n" % \
2621 address)
2622 self.output_find_results(f, unaligned_res)
2623
2624 if len(aligned_res) + len(unaligned_res) == 0:
2625 f.write("<h3>No occurences of 0x%x found in the dump</h3>\n" % address)
2626
2627 self.output_footer(f)
2628
2629 except ValueError:
2630 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
2631 return
2632
2633 def output_disasm_pc(self, f):
2634 address = self.reader.ExceptionIP()
2635 if not self.reader.IsValidAddress(address):
2636 return
2637 self.output_disasm_range(f, address - 16, address + 16, address, True)
2638
2639
2640WEB_DUMPS_HEADER = """
2641<!DOCTYPE html>
2642<html>
2643<head>
2644<meta content="text/html; charset=utf-8" http-equiv="content-type">
2645<style media="screen" type="text/css">
2646
2647.dumplist {
2648 border-collapse : collapse;
2649 border-spacing : 0px;
2650 font-family: monospace;
2651}
2652
2653.dumpcomments {
2654 border : 1px solid LightGray;
2655 width : 32em;
2656}
2657
2658</style>
2659
2660<script type="application/javascript">
2661
2662var dump_str = "dump-";
2663var dump_len = dump_str.length;
2664
2665function dump_comment() {
2666 var s = event.srcElement.id;
2667 var index = s.indexOf(dump_str);
2668 if (index >= 0) {
2669 send_dump_desc(s.substring(index + dump_len), event.srcElement.value);
2670 }
2671}
2672
2673function send_dump_desc(name, desc) {
2674 xmlhttp = new XMLHttpRequest();
2675 name = encodeURIComponent(name)
2676 desc = encodeURIComponent(desc)
2677 xmlhttp.open("GET",
2678 "setdumpdesc?dump=" + name +
2679 "&description=" + desc, true);
2680 xmlhttp.send();
2681}
2682
2683</script>
2684
2685<title>Dump list</title>
2686</head>
2687
2688<body>
2689"""
2690
2691WEB_DUMPS_FOOTER = """
2692</body>
2693</html>
2694"""
2695
2696DUMP_FILE_RE = re.compile(r"[-_0-9a-zA-Z][-\._0-9a-zA-Z]*\.dmp$")
2697
2698
2699class InspectionWebServer(BaseHTTPServer.HTTPServer):
2700 def __init__(self, port_number, switches, minidump_name):
2701 BaseHTTPServer.HTTPServer.__init__(
2702 self, ('', port_number), InspectionWebHandler)
2703 splitpath = os.path.split(minidump_name)
2704 self.dumppath = splitpath[0]
2705 self.dumpfilename = splitpath[1]
2706 self.default_formatter = InspectionWebFormatter(
2707 switches, minidump_name, self)
2708 self.formatters = { self.dumpfilename : self.default_formatter }
2709 self.switches = switches
2710
2711 def output_dump_desc_field(self, f, name):
2712 try:
2713 descfile = open(os.path.join(self.dumppath, name + ".desc"), "r")
2714 desc = descfile.readline()
2715 descfile.close()
2716 except IOError:
2717 desc = ""
2718 f.write("<input type=\"text\" class=\"dumpcomments\" "
2719 "id=\"dump-%s\" onchange=\"dump_comment()\" value=\"%s\">\n" %
2720 (cgi.escape(name), desc))
2721
2722 def set_dump_desc(self, name, description):
2723 if not DUMP_FILE_RE.match(name):
2724 return False
2725 fname = os.path.join(self.dumppath, name)
2726 if not os.path.isfile(fname):
2727 return False
2728 fname = fname + ".desc"
2729 descfile = open(fname, "w")
2730 descfile.write(description)
2731 descfile.close()
2732 return True
2733
2734 def get_dump_formatter(self, name):
2735 if name is None:
2736 return self.default_formatter
2737 else:
2738 if not DUMP_FILE_RE.match(name):
2739 raise WebParameterError("Invalid name '%s'" % name)
2740 formatter = self.formatters.get(name, None)
2741 if formatter is None:
2742 try:
2743 formatter = InspectionWebFormatter(
2744 self.switches, os.path.join(self.dumppath, name), self)
2745 self.formatters[name] = formatter
2746 except IOError:
2747 raise WebParameterError("Could not open dump '%s'" % name)
2748 return formatter
2749
2750 def output_dumps(self, f):
2751 f.write(WEB_DUMPS_HEADER)
2752 f.write("<h3>List of available dumps</h3>")
2753 f.write("<table class=\"dumplist\">\n")
2754 f.write("<thead><tr>")
2755 f.write("<th>Name</th>")
2756 f.write("<th>File time</th>")
2757 f.write("<th>Comment</th>")
2758 f.write("</tr></thead>")
2759 dumps_by_time = {}
2760 for fname in os.listdir(self.dumppath):
2761 if DUMP_FILE_RE.match(fname):
2762 mtime = os.stat(os.path.join(self.dumppath, fname)).st_mtime
2763 fnames = dumps_by_time.get(mtime, [])
2764 fnames.append(fname)
2765 dumps_by_time[mtime] = fnames
2766
2767 for mtime in sorted(dumps_by_time, reverse=True):
2768 fnames = dumps_by_time[mtime]
2769 for fname in fnames:
2770 f.write("<tr>\n")
2771 f.write("<td><a href=\"summary.html?%s\">%s</a></td>\n" % (
2772 (urllib.urlencode({ 'dump' : fname }), fname)))
2773 f.write("<td>&nbsp;&nbsp;&nbsp;")
2774 f.write(datetime.datetime.fromtimestamp(mtime))
2775 f.write("</td>")
2776 f.write("<td>&nbsp;&nbsp;&nbsp;")
2777 self.output_dump_desc_field(f, fname)
2778 f.write("</td>")
2779 f.write("</tr>\n")
2780 f.write("</table>\n")
2781 f.write(WEB_DUMPS_FOOTER)
2782 return
2783
2784class InspectionShell(cmd.Cmd):
2785 def __init__(self, reader, heap):
2786 cmd.Cmd.__init__(self)
2787 self.reader = reader
2788 self.heap = heap
2789 self.padawan = InspectionPadawan(reader, heap)
2790 self.prompt = "(grok) "
2791
2792 def do_da(self, address):
2793 """
2794 Print ASCII string starting at specified address.
2795 """
2796 address = int(address, 16)
2797 string = ""
2798 while self.reader.IsValidAddress(address):
2799 code = self.reader.ReadU8(address)
2800 if code < 128:
2801 string += chr(code)
2802 else:
2803 break
2804 address += 1
2805 if string == "":
2806 print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address)
2807 else:
2808 print "%s\n" % string
2809
2810 def do_dd(self, address):
2811 """
2812 Interpret memory at the given address (if available) as a sequence
2813 of words. Automatic alignment is not performed.
2814 """
2815 start = int(address, 16)
2816 if (start & self.heap.ObjectAlignmentMask()) != 0:
2817 print "Warning: Dumping un-aligned memory, is this what you had in mind?"
2818 for slot in xrange(start,
2819 start + self.reader.PointerSize() * 10,
2820 self.reader.PointerSize()):
2821 if not self.reader.IsValidAddress(slot):
2822 print "Address is not contained within the minidump!"
2823 return
2824 maybe_address = self.reader.ReadUIntPtr(slot)
2825 heap_object = self.padawan.SenseObject(maybe_address)
2826 print "%s: %s %s" % (self.reader.FormatIntPtr(slot),
2827 self.reader.FormatIntPtr(maybe_address),
2828 heap_object or '')
2829
2830 def do_do(self, address):
2831 """
2832 Interpret memory at the given address as a V8 object. Automatic
2833 alignment makes sure that you can pass tagged as well as un-tagged
2834 addresses.
2835 """
2836 address = int(address, 16)
2837 if (address & self.heap.ObjectAlignmentMask()) == 0:
2838 address = address + 1
2839 elif (address & self.heap.ObjectAlignmentMask()) != 1:
2840 print "Address doesn't look like a valid pointer!"
2841 return
2842 heap_object = self.padawan.SenseObject(address)
2843 if heap_object:
2844 heap_object.Print(Printer())
2845 else:
2846 print "Address cannot be interpreted as object!"
2847
2848 def do_do_desc(self, address):
2849 """
2850 Print a descriptor array in a readable format.
2851 """
2852 start = int(address, 16)
2853 if ((start & 1) == 1): start = start - 1
2854 DescriptorArray(FixedArray(self.heap, None, start)).Print(Printer())
2855
2856 def do_do_map(self, address):
2857 """
2858 Print a descriptor array in a readable format.
2859 """
2860 start = int(address, 16)
2861 if ((start & 1) == 1): start = start - 1
2862 Map(self.heap, None, start).Print(Printer())
2863
2864 def do_do_trans(self, address):
2865 """
2866 Print a transition array in a readable format.
2867 """
2868 start = int(address, 16)
2869 if ((start & 1) == 1): start = start - 1
2870 TransitionArray(FixedArray(self.heap, None, start)).Print(Printer())
2871
2872 def do_dp(self, address):
2873 """
2874 Interpret memory at the given address as being on a V8 heap page
2875 and print information about the page header (if available).
2876 """
2877 address = int(address, 16)
2878 page_address = address & ~self.heap.PageAlignmentMask()
2879 if self.reader.IsValidAddress(page_address):
2880 raise NotImplementedError
2881 else:
2882 print "Page header is not available!"
2883
2884 def do_k(self, arguments):
2885 """
2886 Teach V8 heap layout information to the inspector. This increases
2887 the amount of annotations the inspector can produce while dumping
2888 data. The first page of each heap space is of particular interest
2889 because it contains known objects that do not move.
2890 """
2891 self.padawan.PrintKnowledge()
2892
2893 def do_kd(self, address):
2894 """
2895 Teach V8 heap layout information to the inspector. Set the first
2896 data-space page by passing any pointer into that page.
2897 """
2898 address = int(address, 16)
2899 page_address = address & ~self.heap.PageAlignmentMask()
2900 self.padawan.known_first_data_page = page_address
2901
2902 def do_km(self, address):
2903 """
2904 Teach V8 heap layout information to the inspector. Set the first
2905 map-space page by passing any pointer into that page.
2906 """
2907 address = int(address, 16)
2908 page_address = address & ~self.heap.PageAlignmentMask()
2909 self.padawan.known_first_map_page = page_address
2910
2911 def do_kp(self, address):
2912 """
2913 Teach V8 heap layout information to the inspector. Set the first
2914 pointer-space page by passing any pointer into that page.
2915 """
2916 address = int(address, 16)
2917 page_address = address & ~self.heap.PageAlignmentMask()
2918 self.padawan.known_first_pointer_page = page_address
2919
2920 def do_list(self, smth):
2921 """
2922 List all available memory regions.
2923 """
2924 def print_region(reader, start, size, location):
2925 print " %s - %s (%d bytes)" % (reader.FormatIntPtr(start),
2926 reader.FormatIntPtr(start + size),
2927 size)
2928 print "Available memory regions:"
2929 self.reader.ForEachMemoryRegion(print_region)
2930
2931 def do_lm(self, arg):
2932 """
2933 List details for all loaded modules in the minidump. An argument can
2934 be passed to limit the output to only those modules that contain the
2935 argument as a substring (case insensitive match).
2936 """
2937 for module in self.reader.module_list.modules:
2938 if arg:
2939 name = GetModuleName(self.reader, module).lower()
2940 if name.find(arg.lower()) >= 0:
2941 PrintModuleDetails(self.reader, module)
2942 else:
2943 PrintModuleDetails(self.reader, module)
2944 print
2945
2946 def do_s(self, word):
2947 """
2948 Search for a given word in available memory regions. The given word
2949 is expanded to full pointer size and searched at aligned as well as
2950 un-aligned memory locations. Use 'sa' to search aligned locations
2951 only.
2952 """
2953 try:
2954 word = int(word, 0)
2955 except ValueError:
2956 print "Malformed word, prefix with '0x' to use hexadecimal format."
2957 return
2958 print "Searching for word %d/0x%s:" % (word, self.reader.FormatIntPtr(word))
2959 self.reader.FindWord(word)
2960
2961 def do_sh(self, none):
2962 """
2963 Search for the V8 Heap object in all available memory regions. You
2964 might get lucky and find this rare treasure full of invaluable
2965 information.
2966 """
2967 raise NotImplementedError
2968
2969 def do_u(self, args):
2970 """
2971 Unassemble memory in the region [address, address + size). If the
2972 size is not specified, a default value of 32 bytes is used.
2973 Synopsis: u 0x<address> 0x<size>
2974 """
2975 args = args.split(' ')
2976 start = int(args[0], 16)
2977 size = int(args[1], 16) if len(args) > 1 else 0x20
2978 if not self.reader.IsValidAddress(start):
2979 print "Address is not contained within the minidump!"
2980 return
2981 lines = self.reader.GetDisasmLines(start, size)
2982 for line in lines:
2983 print FormatDisasmLine(start, self.heap, line)
2984 print
2985
2986 def do_EOF(self, none):
2987 raise KeyboardInterrupt
Ben Murdoche0cee9b2011-05-25 10:26:03 +01002988
2989EIP_PROXIMITY = 64
2990
Ben Murdoch3ef787d2012-04-12 10:51:47 +01002991CONTEXT_FOR_ARCH = {
2992 MD_CPU_ARCHITECTURE_AMD64:
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002993 ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip',
2994 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'],
2995 MD_CPU_ARCHITECTURE_ARM:
2996 ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
2997 'r10', 'r11', 'r12', 'sp', 'lr', 'pc'],
Ben Murdoch3ef787d2012-04-12 10:51:47 +01002998 MD_CPU_ARCHITECTURE_X86:
2999 ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
3000}
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003001
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003002KNOWN_MODULES = {'chrome.exe', 'chrome.dll'}
3003
3004def GetVersionString(ms, ls):
3005 return "%d.%d.%d.%d" % (ms >> 16, ms & 0xffff, ls >> 16, ls & 0xffff)
3006
3007
3008def GetModuleName(reader, module):
3009 name = reader.ReadMinidumpString(module.module_name_rva)
3010 # simplify for path manipulation
3011 name = name.encode('utf-8')
3012 return str(os.path.basename(str(name).replace("\\", "/")))
3013
3014
3015def PrintModuleDetails(reader, module):
3016 print "%s" % GetModuleName(reader, module)
3017 file_version = GetVersionString(module.version_info.dwFileVersionMS,
3018 module.version_info.dwFileVersionLS)
3019 product_version = GetVersionString(module.version_info.dwProductVersionMS,
3020 module.version_info.dwProductVersionLS)
3021 print " base: %s" % reader.FormatIntPtr(module.base_of_image)
3022 print " end: %s" % reader.FormatIntPtr(module.base_of_image +
3023 module.size_of_image)
3024 print " file version: %s" % file_version
3025 print " product version: %s" % product_version
3026 time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
3027 print " timestamp: %s" % time_date_stamp
3028
3029
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003030def AnalyzeMinidump(options, minidump_name):
3031 reader = MinidumpReader(options, minidump_name)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003032 heap = None
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003033 DebugPrint("========================================")
3034 if reader.exception is None:
3035 print "Minidump has no exception info"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003036 else:
3037 print "Exception info:"
3038 exception_thread = reader.thread_map[reader.exception.thread_id]
3039 print " thread id: %d" % exception_thread.id
3040 print " code: %08X" % reader.exception.exception.code
3041 print " context:"
3042 for r in CONTEXT_FOR_ARCH[reader.arch]:
3043 print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r)))
3044 # TODO(vitalyr): decode eflags.
3045 if reader.arch == MD_CPU_ARCHITECTURE_ARM:
3046 print " cpsr: %s" % bin(reader.exception_context.cpsr)[2:]
3047 else:
3048 print " eflags: %s" % bin(reader.exception_context.eflags)[2:]
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003049
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003050 print
3051 print " modules:"
3052 for module in reader.module_list.modules:
3053 name = GetModuleName(reader, module)
3054 if name in KNOWN_MODULES:
3055 print " %s at %08X" % (name, module.base_of_image)
3056 reader.TryLoadSymbolsFor(name, module)
3057 print
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003058
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003059 stack_top = reader.ExceptionSP()
3060 stack_bottom = exception_thread.stack.start + \
3061 exception_thread.stack.memory.data_size
3062 stack_map = {reader.ExceptionIP(): -1}
3063 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
3064 maybe_address = reader.ReadUIntPtr(slot)
3065 if not maybe_address in stack_map:
3066 stack_map[maybe_address] = slot
3067 heap = V8Heap(reader, stack_map)
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003068
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003069 print "Disassembly around exception.eip:"
3070 eip_symbol = reader.FindSymbol(reader.ExceptionIP())
3071 if eip_symbol is not None:
3072 print eip_symbol
3073 disasm_start = reader.ExceptionIP() - EIP_PROXIMITY
3074 disasm_bytes = 2 * EIP_PROXIMITY
3075 if (options.full):
3076 full_range = reader.FindRegion(reader.ExceptionIP())
3077 if full_range is not None:
3078 disasm_start = full_range[0]
3079 disasm_bytes = full_range[1]
3080
3081 lines = reader.GetDisasmLines(disasm_start, disasm_bytes)
3082
3083 for line in lines:
3084 print FormatDisasmLine(disasm_start, heap, line)
3085 print
3086
3087 if heap is None:
3088 heap = V8Heap(reader, None)
3089
3090 if options.full:
3091 FullDump(reader, heap)
3092
3093 if options.command:
3094 InspectionShell(reader, heap).onecmd(options.command)
3095
3096 if options.shell:
3097 try:
3098 InspectionShell(reader, heap).cmdloop("type help to get help")
3099 except KeyboardInterrupt:
3100 print "Kthxbye."
3101 elif not options.command:
3102 if reader.exception is not None:
3103 frame_pointer = reader.ExceptionFP()
3104 print "Annotated stack (from exception.esp to bottom):"
3105 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
3106 ascii_content = [c if c >= '\x20' and c < '\x7f' else '.'
3107 for c in reader.ReadBytes(slot, reader.PointerSize())]
3108 maybe_address = reader.ReadUIntPtr(slot)
3109 heap_object = heap.FindObject(maybe_address)
3110 maybe_symbol = reader.FindSymbol(maybe_address)
3111 if slot == frame_pointer:
3112 maybe_symbol = "<---- frame pointer"
3113 frame_pointer = maybe_address
3114 print "%s: %s %s %s" % (reader.FormatIntPtr(slot),
3115 reader.FormatIntPtr(maybe_address),
3116 "".join(ascii_content),
3117 maybe_symbol or "")
3118 if heap_object:
3119 heap_object.Print(Printer())
3120 print
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003121
3122 reader.Dispose()
3123
3124
3125if __name__ == "__main__":
3126 parser = optparse.OptionParser(USAGE)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003127 parser.add_option("-s", "--shell", dest="shell", action="store_true",
3128 help="start an interactive inspector shell")
3129 parser.add_option("-w", "--web", dest="web", action="store_true",
3130 help="start a web server on localhost:%i" % PORT_NUMBER)
3131 parser.add_option("-c", "--command", dest="command", default="",
3132 help="run an interactive inspector shell command and exit")
3133 parser.add_option("-f", "--full", dest="full", action="store_true",
3134 help="dump all information contained in the minidump")
3135 parser.add_option("--symdir", dest="symdir", default=".",
3136 help="directory containing *.pdb.sym file with symbols")
3137 parser.add_option("--objdump",
3138 default="/usr/bin/objdump",
3139 help="objdump tool to use [default: %default]")
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003140 options, args = parser.parse_args()
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003141 if os.path.exists(options.objdump):
3142 disasm.OBJDUMP_BIN = options.objdump
3143 OBJDUMP_BIN = options.objdump
3144 else:
3145 print "Cannot find %s, falling back to default objdump" % options.objdump
Ben Murdoche0cee9b2011-05-25 10:26:03 +01003146 if len(args) != 1:
3147 parser.print_help()
3148 sys.exit(1)
Ben Murdochb8a8cc12014-11-26 15:28:44 +00003149 if options.web:
3150 try:
3151 server = InspectionWebServer(PORT_NUMBER, options, args[0])
3152 print 'Started httpserver on port ' , PORT_NUMBER
3153 webbrowser.open('http://localhost:%i/summary.html' % PORT_NUMBER)
3154 server.serve_forever()
3155 except KeyboardInterrupt:
3156 print '^C received, shutting down the web server'
3157 server.socket.close()
3158 else:
3159 AnalyzeMinidump(options, args[0])