Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(692)

Side by Side Diff: tools/grokdump.py

Issue 8957005: Add X64 minidumps support to tools/grokdump.py (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright 2011 the V8 project authors. All rights reserved. 3 # Copyright 2011 the V8 project authors. All rights reserved.
4 # Redistribution and use in source and binary forms, with or without 4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are 5 # modification, are permitted provided that the following conditions are
6 # met: 6 # met:
7 # 7 #
8 # * Redistributions of source code must retain the above copyright 8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer. 9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above 10 # * Redistributions in binary form must reproduce the above
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
45 Shows the processor state at the point of exception including the 45 Shows the processor state at the point of exception including the
46 stack of the active thread and the referenced objects in the V8 46 stack of the active thread and the referenced objects in the V8
47 heap. Code objects are disassembled and the addresses linked from the 47 heap. Code objects are disassembled and the addresses linked from the
48 stack (pushed return addresses) are marked with "=>". 48 stack (pushed return addresses) are marked with "=>".
49 49
50 50
51 Examples: 51 Examples:
52 $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp 52 $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp
53 """ 53 """
54 54
55
55 DEBUG=False 56 DEBUG=False
56 57
57 58
58 def DebugPrint(s): 59 def DebugPrint(s):
59 if not DEBUG: return 60 if not DEBUG: return
60 print s 61 print s
61 62
62 63
63 class Descriptor(object): 64 class Descriptor(object):
64 """Descriptor of a structure in a memory.""" 65 """Descriptor of a structure in a memory."""
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after
226 ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 227 ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
227 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 228 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
228 ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 229 ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
229 ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)), 230 ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
230 # MD_CONTEXT_X86_EXTENDED_REGISTERS. 231 # MD_CONTEXT_X86_EXTENDED_REGISTERS.
231 ("extended_registers", 232 ("extended_registers",
232 EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE, 233 EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE,
233 MD_CONTEXT_X86_EXTENDED_REGISTERS)) 234 MD_CONTEXT_X86_EXTENDED_REGISTERS))
234 ]) 235 ])
235 236
237 MD_CONTEXT_AMD64 = 0x00100000
238 MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001)
239 MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002)
240 MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004)
241 MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008)
242 MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010)
243
244 MINIDUMP_CONTEXT_AMD64 = Descriptor([
245 ("p1_home", ctypes.c_uint64),
246 ("p2_home", ctypes.c_uint64),
247 ("p3_home", ctypes.c_uint64),
248 ("p4_home", ctypes.c_uint64),
249 ("p5_home", ctypes.c_uint64),
250 ("p6_home", ctypes.c_uint64),
251 ("context_flags", ctypes.c_uint32),
252 ("mx_csr", ctypes.c_uint32),
253 # MD_CONTEXT_AMD64_CONTROL.
254 ("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
255 # MD_CONTEXT_AMD64_SEGMENTS
256 ("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
257 ("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
258 ("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
259 ("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
260 # MD_CONTEXT_AMD64_CONTROL.
261 ("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
262 ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)),
263 # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
264 ("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
265 ("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
266 ("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
267 ("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
268 ("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
269 ("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
270 # MD_CONTEXT_AMD64_INTEGER.
271 ("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
272 ("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
273 ("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
274 ("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
275 # MD_CONTEXT_AMD64_CONTROL.
276 ("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
277 # MD_CONTEXT_AMD64_INTEGER.
278 ("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
279 ("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
280 ("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
281 ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
282 ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
283 ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
284 ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
285 ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
286 ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
287 ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
288 ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
289 # MD_CONTEXT_AMD64_CONTROL.
290 ("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
291 # MD_CONTEXT_AMD64_FLOATING_POINT
292 ("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
293 MD_CONTEXT_AMD64_FLOATING_POINT)),
294 ("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
295 MD_CONTEXT_AMD64_FLOATING_POINT)),
296 ("vector_control", EnableOnFlag(ctypes.c_uint64,
297 MD_CONTEXT_AMD64_FLOATING_POINT)),
298 # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
299 ("debug_control", EnableOnFlag(ctypes.c_uint64,
300 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
301 ("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64,
302 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
303 ("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64,
304 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
305 ("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64,
306 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
307 ("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64,
308 MD_CONTEXT_AMD64_DEBUG_REGISTERS))
309 ])
310
236 MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([ 311 MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
237 ("start", ctypes.c_uint64), 312 ("start", ctypes.c_uint64),
238 ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype) 313 ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
239 ]) 314 ])
240 315
241 MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([ 316 MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
242 ("start", ctypes.c_uint64), 317 ("start", ctypes.c_uint64),
243 ("size", ctypes.c_uint64) 318 ("size", ctypes.c_uint64)
244 ]) 319 ])
245 320
(...skipping 16 matching lines...) Expand all
262 ("ted", ctypes.c_uint64), 337 ("ted", ctypes.c_uint64),
263 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype), 338 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype),
264 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype) 339 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
265 ]) 340 ])
266 341
267 MINIDUMP_THREAD_LIST = Descriptor([ 342 MINIDUMP_THREAD_LIST = Descriptor([
268 ("thread_count", ctypes.c_uint32), 343 ("thread_count", ctypes.c_uint32),
269 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count) 344 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
270 ]) 345 ])
271 346
347 MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
348 ("processor_architecture", ctypes.c_uint16)
349 ])
350
351 MD_CPU_ARCHITECTURE_X86 = 0
352 MD_CPU_ARCHITECTURE_AMD64 = 9
272 353
273 class MinidumpReader(object): 354 class MinidumpReader(object):
274 """Minidump (.dmp) reader.""" 355 """Minidump (.dmp) reader."""
275 356
276 _HEADER_MAGIC = 0x504d444d 357 _HEADER_MAGIC = 0x504d444d
277 358
278 def __init__(self, options, minidump_name): 359 def __init__(self, options, minidump_name):
279 self.minidump_name = minidump_name 360 self.minidump_name = minidump_name
280 self.minidump_file = open(minidump_name, "r") 361 self.minidump_file = open(minidump_name, "r")
281 self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE) 362 self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE)
282 self.header = MINIDUMP_HEADER.Read(self.minidump, 0) 363 self.header = MINIDUMP_HEADER.Read(self.minidump, 0)
283 if self.header.signature != MinidumpReader._HEADER_MAGIC: 364 if self.header.signature != MinidumpReader._HEADER_MAGIC:
284 print >>sys.stderr, "Warning: unsupported minidump header magic" 365 print >>sys.stderr, "Warning: unsupported minidump header magic"
285 DebugPrint(self.header) 366 DebugPrint(self.header)
286 directories = [] 367 directories = []
287 offset = self.header.stream_directories_rva 368 offset = self.header.stream_directories_rva
288 for _ in xrange(self.header.stream_count): 369 for _ in xrange(self.header.stream_count):
289 directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset)) 370 directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
290 offset += MINIDUMP_DIRECTORY.size 371 offset += MINIDUMP_DIRECTORY.size
372 self.arch = None
291 self.exception = None 373 self.exception = None
292 self.exception_context = None 374 self.exception_context = None
293 self.memory_list = None 375 self.memory_list = None
294 self.memory_list64 = None 376 self.memory_list64 = None
295 self.thread_map = {} 377 self.thread_map = {}
378
379 # Find MDRawSystemInfo stream and determine arch.
380 for d in directories:
381 if d.stream_type == MD_SYSTEM_INFO_STREAM:
382 system_info = MINIDUMP_RAW_SYSTEM_INFO.Read(
383 self.minidump, d.location.rva)
384 self.arch = system_info.processor_architecture
385 assert self.arch in [MD_CPU_ARCHITECTURE_AMD64, MD_CPU_ARCHITECTURE_X86]
386 assert not self.arch is None
387
296 for d in directories: 388 for d in directories:
297 DebugPrint(d) 389 DebugPrint(d)
298 # TODO(vitalyr): extract system info including CPU features.
299 if d.stream_type == MD_EXCEPTION_STREAM: 390 if d.stream_type == MD_EXCEPTION_STREAM:
300 self.exception = MINIDUMP_EXCEPTION_STREAM.Read( 391 self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
301 self.minidump, d.location.rva) 392 self.minidump, d.location.rva)
302 DebugPrint(self.exception) 393 DebugPrint(self.exception)
303 self.exception_context = MINIDUMP_CONTEXT_X86.Read( 394 if self.arch == MD_CPU_ARCHITECTURE_X86:
304 self.minidump, self.exception.thread_context.rva) 395 self.exception_context = MINIDUMP_CONTEXT_X86.Read(
396 self.minidump, self.exception.thread_context.rva)
397 elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
398 self.exception_context = MINIDUMP_CONTEXT_AMD64.Read(
399 self.minidump, self.exception.thread_context.rva)
Erik Corry 2011/12/15 12:09:11 Do we output an error message if it's not one of t
305 DebugPrint(self.exception_context) 400 DebugPrint(self.exception_context)
306 elif d.stream_type == MD_THREAD_LIST_STREAM: 401 elif d.stream_type == MD_THREAD_LIST_STREAM:
307 thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva) 402 thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
308 assert ctypes.sizeof(thread_list) == d.location.data_size 403 assert ctypes.sizeof(thread_list) == d.location.data_size
309 DebugPrint(thread_list) 404 DebugPrint(thread_list)
310 for thread in thread_list.threads: 405 for thread in thread_list.threads:
311 DebugPrint(thread) 406 DebugPrint(thread)
312 self.thread_map[thread.id] = thread 407 self.thread_map[thread.id] = thread
313 elif d.stream_type == MD_MEMORY_LIST_STREAM: 408 elif d.stream_type == MD_MEMORY_LIST_STREAM:
314 print >>sys.stderr, "Warning: not a full minidump" 409 print >>sys.stderr, "Warning: not a full minidump"
(...skipping 13 matching lines...) Expand all
328 return self.FindLocation(address) is not None 423 return self.FindLocation(address) is not None
329 424
330 def ReadU8(self, address): 425 def ReadU8(self, address):
331 location = self.FindLocation(address) 426 location = self.FindLocation(address)
332 return ctypes.c_uint8.from_buffer(self.minidump, location).value 427 return ctypes.c_uint8.from_buffer(self.minidump, location).value
333 428
334 def ReadU32(self, address): 429 def ReadU32(self, address):
335 location = self.FindLocation(address) 430 location = self.FindLocation(address)
336 return ctypes.c_uint32.from_buffer(self.minidump, location).value 431 return ctypes.c_uint32.from_buffer(self.minidump, location).value
337 432
433 def ReadU64(self, address):
434 location = self.FindLocation(address)
435 return ctypes.c_uint64.from_buffer(self.minidump, location).value
436
437 def ReadUIntPtr(self, address):
438 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
439 return self.ReadU64(address)
440 elif self.arch == MD_CPU_ARCHITECTURE_X86:
441 return self.ReadU32(address)
442
338 def ReadBytes(self, address, size): 443 def ReadBytes(self, address, size):
339 location = self.FindLocation(address) 444 location = self.FindLocation(address)
340 return self.minidump[location:location + size] 445 return self.minidump[location:location + size]
341 446
342 def FindLocation(self, address): 447 def FindLocation(self, address):
343 offset = 0 448 offset = 0
344 if self.memory_list64 is not None: 449 if self.memory_list64 is not None:
345 for r in self.memory_list64.ranges: 450 for r in self.memory_list64.ranges:
346 if r.start <= address < r.start + r.size: 451 if r.start <= address < r.start + r.size:
347 return self.memory_list64.base_rva + offset + address - r.start 452 return self.memory_list64.base_rva + offset + address - r.start
348 offset += r.size 453 offset += r.size
349 if self.memory_list is not None: 454 if self.memory_list is not None:
350 for r in self.memory_list.ranges: 455 for r in self.memory_list.ranges:
351 if r.start <= address < r.start + r.memory.data_size: 456 if r.start <= address < r.start + r.memory.data_size:
352 return r.memory.rva + address - r.start 457 return r.memory.rva + address - r.start
353 return None 458 return None
354 459
355 def GetDisasmLines(self, address, size): 460 def GetDisasmLines(self, address, size):
356 location = self.FindLocation(address) 461 location = self.FindLocation(address)
357 if location is None: return [] 462 if location is None: return []
463 arch = None
464 if self.arch == MD_CPU_ARCHITECTURE_X86:
465 arch = "ia32"
466 elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
467 arch = "x64"
358 return disasm.GetDisasmLines(self.minidump_name, 468 return disasm.GetDisasmLines(self.minidump_name,
359 location, 469 location,
360 size, 470 size,
361 "ia32", 471 arch,
362 False) 472 False)
363 473
364 474
365 def Dispose(self): 475 def Dispose(self):
366 self.minidump.close() 476 self.minidump.close()
367 self.minidump_file.close() 477 self.minidump_file.close()
368 478
479 def ExceptionIP(self):
480 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
481 return self.exception_context.rip
482 elif self.arch == MD_CPU_ARCHITECTURE_X86:
483 return self.exception_context.eip
484
485 def ExceptionSP(self):
486 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
487 return self.exception_context.rsp
488 elif self.arch == MD_CPU_ARCHITECTURE_X86:
489 return self.exception_context.rbp
490
491 def FormatIntPtr(self, value):
492 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
493 return "%016x" % value
494 elif self.arch == MD_CPU_ARCHITECTURE_X86:
495 return "%08x" % value
496
497 def PointerSize(self):
498 if self.arch == MD_CPU_ARCHITECTURE_AMD64:
499 return 8
500 elif self.arch == MD_CPU_ARCHITECTURE_X86:
501 return 4
502
503 def Register(self, name):
504 return self.exception_context.__getattribute__(name)
505
369 506
370 # List of V8 instance types. Obtained by adding the code below to any .cc file. 507 # List of V8 instance types. Obtained by adding the code below to any .cc file.
371 # 508 #
372 # #define DUMP_TYPE(T) printf("%d: \"%s\",\n", T, #T); 509 # #define DUMP_TYPE(T) printf("%d: \"%s\",\n", T, #T);
373 # struct P { 510 # struct P {
374 # P() { 511 # P() {
375 # printf("{\n"); 512 # printf("{\n");
376 # INSTANCE_TYPE_LIST(DUMP_TYPE) 513 # INSTANCE_TYPE_LIST(DUMP_TYPE)
377 # printf("}\n"); 514 # printf("}\n");
378 # } 515 # }
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
494 self.map = map 631 self.map = map
495 self.address = address 632 self.address = address
496 633
497 def Is(self, cls): 634 def Is(self, cls):
498 return isinstance(self, cls) 635 return isinstance(self, cls)
499 636
500 def Print(self, p): 637 def Print(self, p):
501 p.Print(str(self)) 638 p.Print(str(self))
502 639
503 def __str__(self): 640 def __str__(self):
504 return "HeapObject(%08x, %s)" % (self.address, 641 return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address),
505 INSTANCE_TYPES[self.map.instance_type]) 642 INSTANCE_TYPES[self.map.instance_type])
506 643
507 def ObjectField(self, offset): 644 def ObjectField(self, offset):
508 field_value = self.heap.reader.ReadU32(self.address + offset) 645 field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
509 return self.heap.FindObjectOrSmi(field_value) 646 return self.heap.FindObjectOrSmi(field_value)
510 647
511 def SmiField(self, offset): 648 def SmiField(self, offset):
512 field_value = self.heap.reader.ReadU32(self.address + offset) 649 field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
513 assert (field_value & 1) == 0 650 assert (field_value & 1) == 0
514 return field_value / 2 651 return field_value / 2
515 652
516 653
517 class Map(HeapObject): 654 class Map(HeapObject):
518 INSTANCE_TYPE_OFFSET = 8 655 def InstanceTypeOffset():
656 return self.heap.PointerSize() + self.heap.IntSize()
519 657
520 def __init__(self, heap, map, address): 658 def __init__(self, heap, map, address):
521 HeapObject.__init__(self, heap, map, address) 659 HeapObject.__init__(self, heap, map, address)
522 self.instance_type = \ 660 self.instance_type = \
523 heap.reader.ReadU8(self.address + Map.INSTANCE_TYPE_OFFSET) 661 heap.reader.ReadU8(self.address + self.InstanceTypeOffset())
524 662
525 663
526 class String(HeapObject): 664 class String(HeapObject):
527 LENGTH_OFFSET = 4 665 def LengthOffset(self):
666 return self.heap.PointerSize()
528 667
529 def __init__(self, heap, map, address): 668 def __init__(self, heap, map, address):
530 HeapObject.__init__(self, heap, map, address) 669 HeapObject.__init__(self, heap, map, address)
531 self.length = self.SmiField(String.LENGTH_OFFSET) 670 self.length = self.SmiField(self.LengthOffset())
532 671
533 def GetChars(self): 672 def GetChars(self):
534 return "?string?" 673 return "?string?"
535 674
536 def Print(self, p): 675 def Print(self, p):
537 p.Print(str(self)) 676 p.Print(str(self))
538 677
539 def __str__(self): 678 def __str__(self):
540 return "\"%s\"" % self.GetChars() 679 return "\"%s\"" % self.GetChars()
541 680
542 681
543 class SeqString(String): 682 class SeqString(String):
544 CHARS_OFFSET = 12 683 def CharsOffset(self):
684 return self.heap.PointerSize() * 3
545 685
546 def __init__(self, heap, map, address): 686 def __init__(self, heap, map, address):
547 String.__init__(self, heap, map, address) 687 String.__init__(self, heap, map, address)
548 self.chars = heap.reader.ReadBytes(self.address + SeqString.CHARS_OFFSET, 688 self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(),
549 self.length) 689 self.length)
550 690
551 def GetChars(self): 691 def GetChars(self):
552 return self.chars 692 return self.chars
553 693
554 694
555 class ExternalString(String): 695 class ExternalString(String):
696 # TODO(vegorov) fix ExternalString for X64 architecture
556 RESOURCE_OFFSET = 12 697 RESOURCE_OFFSET = 12
557 698
558 WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4 699 WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
559 WEBKIT_STRING_IMPL_CHARS_OFFSET = 8 700 WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
560 701
561 def __init__(self, heap, map, address): 702 def __init__(self, heap, map, address):
562 String.__init__(self, heap, map, address) 703 String.__init__(self, heap, map, address)
563 reader = heap.reader 704 reader = heap.reader
564 self.resource = \ 705 self.resource = \
565 reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET) 706 reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
566 self.chars = "?external string?" 707 self.chars = "?external string?"
567 if not reader.IsValidAddress(self.resource): return 708 if not reader.IsValidAddress(self.resource): return
568 string_impl_address = self.resource + \ 709 string_impl_address = self.resource + \
569 ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET 710 ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
570 if not reader.IsValidAddress(string_impl_address): return 711 if not reader.IsValidAddress(string_impl_address): return
571 string_impl = reader.ReadU32(string_impl_address) 712 string_impl = reader.ReadU32(string_impl_address)
572 chars_ptr_address = string_impl + \ 713 chars_ptr_address = string_impl + \
573 ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET 714 ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
574 if not reader.IsValidAddress(chars_ptr_address): return 715 if not reader.IsValidAddress(chars_ptr_address): return
575 chars_ptr = reader.ReadU32(chars_ptr_address) 716 chars_ptr = reader.ReadU32(chars_ptr_address)
576 if not reader.IsValidAddress(chars_ptr): return 717 if not reader.IsValidAddress(chars_ptr): return
577 raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length) 718 raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
578 self.chars = codecs.getdecoder("utf16")(raw_chars)[0] 719 self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
579 720
580 def GetChars(self): 721 def GetChars(self):
581 return self.chars 722 return self.chars
582 723
583 724
584 class ConsString(String): 725 class ConsString(String):
585 LEFT_OFFSET = 12 726 def LeftOffset(self):
586 RIGHT_OFFSET = 16 727 return self.heap.PointerSize() * 3
728
729 def RightOffset(self):
730 return self.heap.PointerSize() * 4
587 731
588 def __init__(self, heap, map, address): 732 def __init__(self, heap, map, address):
589 String.__init__(self, heap, map, address) 733 String.__init__(self, heap, map, address)
590 self.left = self.ObjectField(ConsString.LEFT_OFFSET) 734 self.left = self.ObjectField(self.LeftOffset())
591 self.right = self.ObjectField(ConsString.RIGHT_OFFSET) 735 self.right = self.ObjectField(self.RightOffset())
592 736
593 def GetChars(self): 737 def GetChars(self):
594 return self.left.GetChars() + self.right.GetChars() 738 return self.left.GetChars() + self.right.GetChars()
595 739
596 740
597 class Oddball(HeapObject): 741 class Oddball(HeapObject):
598 TO_STRING_OFFSET = 4 742 def ToStringOffset(self):
743 return self.heap.PointerSize()
599 744
600 def __init__(self, heap, map, address): 745 def __init__(self, heap, map, address):
601 HeapObject.__init__(self, heap, map, address) 746 HeapObject.__init__(self, heap, map, address)
602 self.to_string = self.ObjectField(Oddball.TO_STRING_OFFSET) 747 self.to_string = self.ObjectField(self.ToStringOffset())
603 748
604 def Print(self, p): 749 def Print(self, p):
605 p.Print(str(self)) 750 p.Print(str(self))
606 751
607 def __str__(self): 752 def __str__(self):
608 return "<%s>" % self.to_string.GetChars() 753 return "<%s>" % self.to_string.GetChars()
609 754
610 755
611 class FixedArray(HeapObject): 756 class FixedArray(HeapObject):
612 LENGTH_OFFSET = 4 757 def LengthOffset(self):
613 ELEMENTS_OFFSET = 8 758 return self.heap.PointerSize()
759
760 def ElementsOffset(self):
761 return self.heap.PointerSize() * 2
614 762
615 def __init__(self, heap, map, address): 763 def __init__(self, heap, map, address):
616 HeapObject.__init__(self, heap, map, address) 764 HeapObject.__init__(self, heap, map, address)
617 self.length = self.SmiField(FixedArray.LENGTH_OFFSET) 765 self.length = self.SmiField(self.LengthOffset())
618 766
619 def Print(self, p): 767 def Print(self, p):
620 p.Print("FixedArray(%08x) {" % self.address) 768 p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address))
621 p.Indent() 769 p.Indent()
622 p.Print("length: %d" % self.length) 770 p.Print("length: %d" % self.length)
771 base_offset = self.ElementsOffset()
623 for i in xrange(self.length): 772 for i in xrange(self.length):
624 offset = FixedArray.ELEMENTS_OFFSET + 4 * i 773 offset = base_offset + 4 * i
625 p.Print("[%08d] = %s" % (i, self.ObjectField(offset))) 774 p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
626 p.Dedent() 775 p.Dedent()
627 p.Print("}") 776 p.Print("}")
628 777
629 def __str__(self): 778 def __str__(self):
630 return "FixedArray(%08x, length=%d)" % (self.address, self.length) 779 return "FixedArray(%08x, length=%d)" % (self.address, self.length)
631 780
632 781
633 class JSFunction(HeapObject): 782 class JSFunction(HeapObject):
634 CODE_ENTRY_OFFSET = 12 783 def CodeEntryOffset(self):
635 SHARED_OFFSET = 20 784 return 3 * self.heap.PointerSize()
785
786 def SharedOffset(self):
787 return 5 * self.heap.PointerSize()
636 788
637 def __init__(self, heap, map, address): 789 def __init__(self, heap, map, address):
638 HeapObject.__init__(self, heap, map, address) 790 HeapObject.__init__(self, heap, map, address)
639 code_entry = \ 791 code_entry = \
640 heap.reader.ReadU32(self.address + JSFunction.CODE_ENTRY_OFFSET) 792 heap.reader.ReadU32(self.address + self.CodeEntryOffset())
641 self.code = heap.FindObject(code_entry - Code.ENTRY_OFFSET + 1) 793 self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1)
642 self.shared = self.ObjectField(JSFunction.SHARED_OFFSET) 794 self.shared = self.ObjectField(self.SharedOffset())
643 795
644 def Print(self, p): 796 def Print(self, p):
645 source = "\n".join(" %s" % line for line in self._GetSource().split("\n")) 797 source = "\n".join(" %s" % line for line in self._GetSource().split("\n"))
646 p.Print("JSFunction(%08x) {" % self.address) 798 p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address))
647 p.Indent() 799 p.Indent()
648 p.Print("inferred name: %s" % self.shared.inferred_name) 800 p.Print("inferred name: %s" % self.shared.inferred_name)
649 if self.shared.script.Is(Script) and self.shared.script.name.Is(String): 801 if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
650 p.Print("script name: %s" % self.shared.script.name) 802 p.Print("script name: %s" % self.shared.script.name)
651 p.Print("source:") 803 p.Print("source:")
652 p.PrintLines(self._GetSource().split("\n")) 804 p.PrintLines(self._GetSource().split("\n"))
653 p.Print("code:") 805 p.Print("code:")
654 self.code.Print(p) 806 self.code.Print(p)
655 if self.code != self.shared.code: 807 if self.code != self.shared.code:
656 p.Print("unoptimized code:") 808 p.Print("unoptimized code:")
657 self.shared.code.Print(p) 809 self.shared.code.Print(p)
658 p.Dedent() 810 p.Dedent()
659 p.Print("}") 811 p.Print("}")
660 812
661 def __str__(self): 813 def __str__(self):
662 inferred_name = "" 814 inferred_name = ""
663 if self.shared.Is(SharedFunctionInfo): 815 if self.shared.Is(SharedFunctionInfo):
664 inferred_name = self.shared.inferred_name 816 inferred_name = self.shared.inferred_name
665 return "JSFunction(%08x, %s)" % (self.address, inferred_name) 817 return "JSFunction(%s, %s)" % \
818 (self.heap.reader.FormatIntPtr(self.address), inferred_name)
666 819
667 def _GetSource(self): 820 def _GetSource(self):
668 source = "?source?" 821 source = "?source?"
669 start = self.shared.start_position 822 start = self.shared.start_position
670 end = self.shared.end_position 823 end = self.shared.end_position
671 if not self.shared.script.Is(Script): return source 824 if not self.shared.script.Is(Script): return source
672 script_source = self.shared.script.source 825 script_source = self.shared.script.source
673 if not script_source.Is(String): return source 826 if not script_source.Is(String): return source
674 return script_source.GetChars()[start:end] 827 return script_source.GetChars()[start:end]
675 828
676 829
677 class SharedFunctionInfo(HeapObject): 830 class SharedFunctionInfo(HeapObject):
678 CODE_OFFSET = 2 * 4 831 def CodeOffset(self):
679 SCRIPT_OFFSET = 7 * 4 832 return 2 * self.heap.PointerSize()
680 INFERRED_NAME_OFFSET = 9 * 4 833
681 START_POSITION_AND_TYPE_OFFSET = 17 * 4 834 def ScriptOffset(self):
682 END_POSITION_OFFSET = 18 * 4 835 return 7 * self.heap.PointerSize()
836
837 def InferredNameOffset(self):
838 return 9 * self.heap.PointerSize()
839
840 def EndPositionOffset(self):
841 return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
842
843 def StartPositionAndTypeOffset(self):
844 return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
683 845
684 def __init__(self, heap, map, address): 846 def __init__(self, heap, map, address):
685 HeapObject.__init__(self, heap, map, address) 847 HeapObject.__init__(self, heap, map, address)
686 self.code = self.ObjectField(SharedFunctionInfo.CODE_OFFSET) 848 self.code = self.ObjectField(self.CodeOffset())
687 self.script = self.ObjectField(SharedFunctionInfo.SCRIPT_OFFSET) 849 self.script = self.ObjectField(self.ScriptOffset())
688 self.inferred_name = \ 850 self.inferred_name = self.ObjectField(self.InferredNameOffset())
689 self.ObjectField(SharedFunctionInfo.INFERRED_NAME_OFFSET) 851 if heap.PointerSize() == 8:
690 start_position_and_type = \ 852 start_position_and_type = \
691 self.SmiField(SharedFunctionInfo.START_POSITION_AND_TYPE_OFFSET) 853 heap.reader.ReadU32(self.StartPositionAndTypeOffset())
692 self.start_position = start_position_and_type >> 2 854 self.start_position = start_position_and_type >> 2
693 self.end_position = self.SmiField(SharedFunctionInfo.END_POSITION_OFFSET) 855 pseudo_smi_end_position = \
856 heap.reader.ReadU32(self.EndPositionOffset())
857 self.end_position = pseudo_smi_end_position >> 2
858 else:
859 start_position_and_type = \
860 self.SmiField(self.StartPositionAndTypeOffset())
861 self.start_position = start_position_and_type >> 2
862 self.end_position = \
863 self.SmiField(self.EndPositionOffset())
694 864
695 865
696 class Script(HeapObject): 866 class Script(HeapObject):
697 SOURCE_OFFSET = 4 867 def SourceOffset(self):
698 NAME_OFFSET = 8 868 return self.heap.PointerSize()
869
870 def NameOffset(self):
871 return self.SourceOffset() + self.heap.PointerSize()
699 872
700 def __init__(self, heap, map, address): 873 def __init__(self, heap, map, address):
701 HeapObject.__init__(self, heap, map, address) 874 HeapObject.__init__(self, heap, map, address)
702 self.source = self.ObjectField(Script.SOURCE_OFFSET) 875 self.source = self.ObjectField(self.SourceOffset())
703 self.name = self.ObjectField(Script.NAME_OFFSET) 876 self.name = self.ObjectField(self.NameOffset())
704 877
705 878
706 class Code(HeapObject): 879 class Code(HeapObject):
707 INSTRUCTION_SIZE_OFFSET = 4 880 CODE_ALIGNMENT_MASK = (1 << 5) - 1
708 ENTRY_OFFSET = 32 881
882 def InstructionSizeOffset(self):
883 return self.heap.PointerSize()
884
885 @staticmethod
886 def HeaderSize(heap):
887 return (heap.PointerSize() + heap.IntSize() + \
888 4 * heap.PointerSize() + 3 * heap.IntSize() + \
889 CODE_ALIGNMENT_MASK) & ~CODE_ALIGNMENT_MASK
709 890
710 def __init__(self, heap, map, address): 891 def __init__(self, heap, map, address):
711 HeapObject.__init__(self, heap, map, address) 892 HeapObject.__init__(self, heap, map, address)
712 self.entry = self.address + Code.ENTRY_OFFSET 893 self.entry = self.address + Code.HeaderSize(heap)
713 self.instruction_size = \ 894 self.instruction_size = \
714 heap.reader.ReadU32(self.address + Code.INSTRUCTION_SIZE_OFFSET) 895 heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
715 896
716 def Print(self, p): 897 def Print(self, p):
717 lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size) 898 lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
718 p.Print("Code(%08x) {" % self.address) 899 p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address))
719 p.Indent() 900 p.Indent()
720 p.Print("instruction_size: %d" % self.instruction_size) 901 p.Print("instruction_size: %d" % self.instruction_size)
721 p.PrintLines(self._FormatLine(line) for line in lines) 902 p.PrintLines(self._FormatLine(line) for line in lines)
722 p.Dedent() 903 p.Dedent()
723 p.Print("}") 904 p.Print("}")
724 905
725 def _FormatLine(self, line): 906 def _FormatLine(self, line):
726 return FormatDisasmLine(self.entry, self.heap, line) 907 return FormatDisasmLine(self.entry, self.heap, line)
727 908
728 909
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
760 def FindObjectOrSmi(self, tagged_address): 941 def FindObjectOrSmi(self, tagged_address):
761 if (tagged_address & 1) == 0: return tagged_address / 2 942 if (tagged_address & 1) == 0: return tagged_address / 2
762 return self.FindObject(tagged_address) 943 return self.FindObject(tagged_address)
763 944
764 def FindObject(self, tagged_address): 945 def FindObject(self, tagged_address):
765 if tagged_address in self.objects: 946 if tagged_address in self.objects:
766 return self.objects[tagged_address] 947 return self.objects[tagged_address]
767 if (tagged_address & 1) != 1: return None 948 if (tagged_address & 1) != 1: return None
768 address = tagged_address - 1 949 address = tagged_address - 1
769 if not self.reader.IsValidAddress(address): return None 950 if not self.reader.IsValidAddress(address): return None
770 map_tagged_address = self.reader.ReadU32(address) 951 map_tagged_address = self.reader.ReadUIntPtr(address)
771 if tagged_address == map_tagged_address: 952 if tagged_address == map_tagged_address:
772 # Meta map? 953 # Meta map?
773 meta_map = Map(self, None, address) 954 meta_map = Map(self, None, address)
774 instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type) 955 instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
775 if instance_type_name != "MAP_TYPE": return None 956 if instance_type_name != "MAP_TYPE": return None
776 meta_map.map = meta_map 957 meta_map.map = meta_map
777 object = meta_map 958 object = meta_map
778 else: 959 else:
779 map = self.FindObject(map_tagged_address) 960 map = self.FindObject(map_tagged_address)
780 if map is None: return None 961 if map is None: return None
781 instance_type_name = INSTANCE_TYPES.get(map.instance_type) 962 instance_type_name = INSTANCE_TYPES.get(map.instance_type)
782 if instance_type_name is None: return None 963 if instance_type_name is None: return None
783 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject) 964 cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
784 object = cls(self, map, address) 965 object = cls(self, map, address)
785 self.objects[tagged_address] = object 966 self.objects[tagged_address] = object
786 return object 967 return object
787 968
969 def PointerSize(self):
970 return self.reader.PointerSize()
971
972
788 973
789 EIP_PROXIMITY = 64 974 EIP_PROXIMITY = 64
790 975
976 CONTEXT_FOR_ARCH = {
977 MD_CPU_ARCHITECTURE_AMD64:
978 ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip'],
979 MD_CPU_ARCHITECTURE_X86:
980 ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
981 }
791 982
792 def AnalyzeMinidump(options, minidump_name): 983 def AnalyzeMinidump(options, minidump_name):
793 reader = MinidumpReader(options, minidump_name) 984 reader = MinidumpReader(options, minidump_name)
794 DebugPrint("========================================") 985 DebugPrint("========================================")
795 if reader.exception is None: 986 if reader.exception is None:
796 print "Minidump has no exception info" 987 print "Minidump has no exception info"
797 return 988 return
798 print "Exception info:" 989 print "Exception info:"
799 exception_thread = reader.thread_map[reader.exception.thread_id] 990 exception_thread = reader.thread_map[reader.exception.thread_id]
800 print " thread id: %d" % exception_thread.id 991 print " thread id: %d" % exception_thread.id
801 print " code: %08X" % reader.exception.exception.code 992 print " code: %08X" % reader.exception.exception.code
802 print " context:" 993 print " context:"
803 print " eax: %08x" % reader.exception_context.eax 994 for r in CONTEXT_FOR_ARCH[reader.arch]:
804 print " ebx: %08x" % reader.exception_context.ebx 995 print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r)))
805 print " ecx: %08x" % reader.exception_context.ecx
806 print " edx: %08x" % reader.exception_context.edx
807 print " edi: %08x" % reader.exception_context.edi
808 print " esi: %08x" % reader.exception_context.esi
809 print " ebp: %08x" % reader.exception_context.ebp
810 print " esp: %08x" % reader.exception_context.esp
811 print " eip: %08x" % reader.exception_context.eip
812 # TODO(vitalyr): decode eflags. 996 # TODO(vitalyr): decode eflags.
813 print " eflags: %s" % bin(reader.exception_context.eflags)[2:] 997 print " eflags: %s" % bin(reader.exception_context.eflags)[2:]
814 print 998 print
815 999
1000 stack_top = reader.ExceptionSP()
816 stack_bottom = exception_thread.stack.start + \ 1001 stack_bottom = exception_thread.stack.start + \
817 exception_thread.stack.memory.data_size 1002 exception_thread.stack.memory.data_size
818 stack_map = {reader.exception_context.eip: -1} 1003 stack_map = {reader.ExceptionIP(): -1}
819 for slot in xrange(reader.exception_context.esp, stack_bottom, 4): 1004 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
820 maybe_address = reader.ReadU32(slot) 1005 maybe_address = reader.ReadUIntPtr(slot)
821 if not maybe_address in stack_map: 1006 if not maybe_address in stack_map:
822 stack_map[maybe_address] = slot 1007 stack_map[maybe_address] = slot
823 heap = V8Heap(reader, stack_map) 1008 heap = V8Heap(reader, stack_map)
824 1009
825 print "Disassembly around exception.eip:" 1010 print "Disassembly around exception.eip:"
826 start = reader.exception_context.eip - EIP_PROXIMITY 1011 start = reader.ExceptionIP() - EIP_PROXIMITY
827 lines = reader.GetDisasmLines(start, 2 * EIP_PROXIMITY) 1012 lines = reader.GetDisasmLines(start, 2 * EIP_PROXIMITY)
828 for line in lines: 1013 for line in lines:
829 print FormatDisasmLine(start, heap, line) 1014 print FormatDisasmLine(start, heap, line)
830 print 1015 print
831 1016
832 print "Annotated stack (from exception.esp to bottom):" 1017 print "Annotated stack (from exception.esp to bottom):"
833 for slot in xrange(reader.exception_context.esp, stack_bottom, 4): 1018 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
834 maybe_address = reader.ReadU32(slot) 1019 maybe_address = reader.ReadUIntPtr(slot)
835 heap_object = heap.FindObject(maybe_address) 1020 heap_object = heap.FindObject(maybe_address)
836 print "%08x: %08x" % (slot, maybe_address) 1021 print "%s: %s" % (reader.FormatIntPtr(slot),
1022 reader.FormatIntPtr(maybe_address))
837 if heap_object: 1023 if heap_object:
838 heap_object.Print(Printer()) 1024 heap_object.Print(Printer())
839 print 1025 print
840 1026
841 reader.Dispose() 1027 reader.Dispose()
842 1028
843 1029
844 if __name__ == "__main__": 1030 if __name__ == "__main__":
845 parser = optparse.OptionParser(USAGE) 1031 parser = optparse.OptionParser(USAGE)
846 options, args = parser.parse_args() 1032 options, args = parser.parse_args()
847 if len(args) != 1: 1033 if len(args) != 1:
848 parser.print_help() 1034 parser.print_help()
849 sys.exit(1) 1035 sys.exit(1)
850 AnalyzeMinidump(options, args[0]) 1036 AnalyzeMinidump(options, args[0])
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698