OLD | NEW |
---|---|
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 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
104 | 104 |
105 def __str__(self): | 105 def __str__(self): |
106 return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field)) | 106 return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field)) |
107 for field, _ in Raw._fields_) + "}" | 107 for field, _ in Raw._fields_) + "}" |
108 return Raw | 108 return Raw |
109 | 109 |
110 | 110 |
111 def do_dump(reader, heap): | 111 def do_dump(reader, heap): |
112 """Dump all available memory regions.""" | 112 """Dump all available memory regions.""" |
113 def dump_region(reader, start, size, location): | 113 def dump_region(reader, start, size, location): |
114 print "%s - %s" % (reader.FormatIntPtr(start), | 114 print |
115 reader.FormatIntPtr(start + size)) | 115 while start & 3 != 0: |
116 for slot in xrange(start, | 116 start += 1 |
117 start + size, | 117 size -= 1 |
118 reader.PointerSize()): | 118 location += 1 |
119 maybe_address = reader.ReadUIntPtr(slot) | 119 is_executable = reader.IsExecutableRegion(location, size) |
120 heap_object = heap.FindObject(maybe_address) | 120 is_ascii = reader.IsASCIIRegion(location, size) |
121 print "%s: %s" % (reader.FormatIntPtr(slot), | 121 |
122 reader.FormatIntPtr(maybe_address)) | 122 if is_executable is not False: |
123 if heap_object: | 123 lines = reader.GetDisasmLines(start, size) |
124 heap_object.Print(Printer()) | 124 for line in lines: |
125 print | 125 print FormatDisasmLine(start, heap, line) |
126 print | |
127 | |
128 if is_ascii is not False: | |
129 # Output in the same format as the Unix hd command | |
130 addr = start | |
131 for slot in xrange(location, location + size, 16): | |
132 hex_line = "" | |
133 asc_line = "" | |
134 for i in xrange(0, 16): | |
135 if slot + i < location + size: | |
136 byte = ctypes.c_uint8.from_buffer(reader.minidump, slot + i).value | |
137 if byte >= 0x20 and byte < 0x7f: | |
138 asc_line += chr(byte) | |
139 else: | |
140 asc_line += "." | |
141 hex_line += " %02x" % (byte) | |
142 else: | |
143 hex_line += " " | |
144 if i == 7: | |
145 hex_line += " " | |
146 print "%s %s |%s|" % (reader.FormatIntPtr(addr), | |
147 hex_line, | |
148 asc_line) | |
149 addr += 16 | |
150 | |
151 if is_executable is not True and is_ascii is not True: | |
152 print "%s - %s" % (reader.FormatIntPtr(start), | |
153 reader.FormatIntPtr(start + size)) | |
154 for slot in xrange(start, | |
155 start + size, | |
156 reader.PointerSize()): | |
157 maybe_address = reader.ReadUIntPtr(slot) | |
158 heap_object = heap.FindObject(maybe_address) | |
159 print "%s: %s" % (reader.FormatIntPtr(slot), | |
160 reader.FormatIntPtr(maybe_address)) | |
161 if heap_object: | |
162 heap_object.Print(Printer()) | |
163 print | |
126 | 164 |
127 reader.ForEachMemoryRegion(dump_region) | 165 reader.ForEachMemoryRegion(dump_region) |
128 | 166 |
129 # Set of structures and constants that describe the layout of minidump | 167 # Set of structures and constants that describe the layout of minidump |
130 # files. Based on MSDN and Google Breakpad. | 168 # files. Based on MSDN and Google Breakpad. |
131 | 169 |
132 MINIDUMP_HEADER = Descriptor([ | 170 MINIDUMP_HEADER = Descriptor([ |
133 ("signature", ctypes.c_uint32), | 171 ("signature", ctypes.c_uint32), |
134 ("version", ctypes.c_uint32), | 172 ("version", ctypes.c_uint32), |
135 ("stream_count", ctypes.c_uint32), | 173 ("stream_count", ctypes.c_uint32), |
(...skipping 327 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
463 def ReadBytes(self, address, size): | 501 def ReadBytes(self, address, size): |
464 location = self.FindLocation(address) | 502 location = self.FindLocation(address) |
465 return self.minidump[location:location + size] | 503 return self.minidump[location:location + size] |
466 | 504 |
467 def _ReadWord(self, location): | 505 def _ReadWord(self, location): |
468 if self.arch == MD_CPU_ARCHITECTURE_AMD64: | 506 if self.arch == MD_CPU_ARCHITECTURE_AMD64: |
469 return ctypes.c_uint64.from_buffer(self.minidump, location).value | 507 return ctypes.c_uint64.from_buffer(self.minidump, location).value |
470 elif self.arch == MD_CPU_ARCHITECTURE_X86: | 508 elif self.arch == MD_CPU_ARCHITECTURE_X86: |
471 return ctypes.c_uint32.from_buffer(self.minidump, location).value | 509 return ctypes.c_uint32.from_buffer(self.minidump, location).value |
472 | 510 |
511 def IsASCIIRegion(self, location, length): | |
Michael Starzinger
2012/05/21 12:03:13
Maybe we should rename this to IsProbableASCIIRegi
| |
512 ascii_bytes = 0 | |
513 non_ascii_bytes = 0 | |
514 for loc in xrange(location, location + length): | |
515 byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value | |
516 if byte >= 0x7f: | |
517 non_ascii_bytes += 1 | |
518 if byte < 0x20 and byte != 0: | |
519 non_ascii_bytes += 1 | |
520 if byte < 0x7f and byte >= 0x20: | |
521 ascii_bytes += 1 | |
522 if byte == 0xa: # newline | |
523 ascii_bytes += 1 | |
524 if ascii_bytes * 10 <= length: | |
525 return False | |
526 if length > 0 and ascii_bytes > non_ascii_bytes * 7: | |
527 return True | |
528 if ascii_bytes > non_ascii_bytes * 3: | |
529 return None # Maybe | |
530 return False | |
531 | |
532 def IsExecutableRegion(self, location, length): | |
Michael Starzinger
2012/05/21 12:03:13
Likewise.
| |
533 opcode_bytes = 0 | |
534 sixty_four = self.arch == MD_CPU_ARCHITECTURE_AMD64 | |
535 for loc in xrange(location, location + length): | |
536 byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value | |
537 if (byte == 0x8b or # mov | |
538 byte == 0x89 or # mov reg-reg | |
539 (byte & 0xf0) == 0x50 or # push/pop | |
540 (sixty_four and (byte & 0xf0) == 0x40) or # rex prefix | |
541 byte == 0xc3 or # return | |
542 byte == 0x74 or # jeq | |
543 byte == 0x84 or # jeq far | |
544 byte == 0x75 or # jne | |
545 byte == 0x85 or # jne far | |
546 byte == 0xe8 or # call | |
547 byte == 0xe9 or # jmp far | |
548 byte == 0xeb): # jmp near | |
549 opcode_bytes += 1 | |
550 opcode_percent = (opcode_bytes * 100) / length | |
551 threshold = 20 | |
552 if opcode_percent > threshold + 2: | |
553 return True | |
554 if opcode_percent > threshold - 2: | |
555 return None # Maybe | |
556 return False | |
557 | |
558 def FindRegion(self, addr): | |
559 answer = [-1, -1] | |
560 def is_in(reader, start, size, location): | |
561 if addr >= start and addr < start + size: | |
562 answer[0] = start | |
563 answer[1] = size | |
564 self.ForEachMemoryRegion(is_in) | |
565 if answer[0] == -1: | |
566 return None | |
567 return answer | |
568 | |
473 def ForEachMemoryRegion(self, cb): | 569 def ForEachMemoryRegion(self, cb): |
474 if self.memory_list64 is not None: | 570 if self.memory_list64 is not None: |
475 for r in self.memory_list64.ranges: | 571 for r in self.memory_list64.ranges: |
476 location = self.memory_list64.base_rva + offset | 572 location = self.memory_list64.base_rva + offset |
477 cb(self, r.start, r.size, location) | 573 cb(self, r.start, r.size, location) |
478 offset += r.size | 574 offset += r.size |
479 | 575 |
480 if self.memory_list is not None: | 576 if self.memory_list is not None: |
481 for r in self.memory_list.ranges: | 577 for r in self.memory_list.ranges: |
482 cb(self, r.start, r.memory.data_size, r.memory.rva) | 578 cb(self, r.start, r.memory.data_size, r.memory.rva) |
(...skipping 609 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1092 def do_list(self, smth): | 1188 def do_list(self, smth): |
1093 """List all available memory regions.""" | 1189 """List all available memory regions.""" |
1094 def print_region(reader, start, size, location): | 1190 def print_region(reader, start, size, location): |
1095 print "%s - %s" % (reader.FormatIntPtr(start), | 1191 print "%s - %s" % (reader.FormatIntPtr(start), |
1096 reader.FormatIntPtr(start + size)) | 1192 reader.FormatIntPtr(start + size)) |
1097 | 1193 |
1098 self.reader.ForEachMemoryRegion(print_region) | 1194 self.reader.ForEachMemoryRegion(print_region) |
1099 | 1195 |
1100 def AnalyzeMinidump(options, minidump_name): | 1196 def AnalyzeMinidump(options, minidump_name): |
1101 reader = MinidumpReader(options, minidump_name) | 1197 reader = MinidumpReader(options, minidump_name) |
1198 heap = None | |
1102 DebugPrint("========================================") | 1199 DebugPrint("========================================") |
1103 if reader.exception is None: | 1200 if reader.exception is None: |
1104 print "Minidump has no exception info" | 1201 print "Minidump has no exception info" |
1105 return | 1202 else: |
1106 print "Exception info:" | 1203 print "Exception info:" |
1107 exception_thread = reader.thread_map[reader.exception.thread_id] | 1204 exception_thread = reader.thread_map[reader.exception.thread_id] |
1108 print " thread id: %d" % exception_thread.id | 1205 print " thread id: %d" % exception_thread.id |
1109 print " code: %08X" % reader.exception.exception.code | 1206 print " code: %08X" % reader.exception.exception.code |
1110 print " context:" | 1207 print " context:" |
1111 for r in CONTEXT_FOR_ARCH[reader.arch]: | 1208 for r in CONTEXT_FOR_ARCH[reader.arch]: |
1112 print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r))) | 1209 print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r))) |
1113 # TODO(vitalyr): decode eflags. | 1210 # TODO(vitalyr): decode eflags. |
1114 print " eflags: %s" % bin(reader.exception_context.eflags)[2:] | 1211 print " eflags: %s" % bin(reader.exception_context.eflags)[2:] |
1115 print | 1212 print |
1116 | 1213 |
1117 stack_top = reader.ExceptionSP() | 1214 stack_top = reader.ExceptionSP() |
1118 stack_bottom = exception_thread.stack.start + \ | 1215 stack_bottom = exception_thread.stack.start + \ |
1119 exception_thread.stack.memory.data_size | 1216 exception_thread.stack.memory.data_size |
1120 stack_map = {reader.ExceptionIP(): -1} | 1217 stack_map = {reader.ExceptionIP(): -1} |
1121 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): | 1218 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): |
1122 maybe_address = reader.ReadUIntPtr(slot) | 1219 maybe_address = reader.ReadUIntPtr(slot) |
1123 if not maybe_address in stack_map: | 1220 if not maybe_address in stack_map: |
1124 stack_map[maybe_address] = slot | 1221 stack_map[maybe_address] = slot |
1125 heap = V8Heap(reader, stack_map) | 1222 heap = V8Heap(reader, stack_map) |
1126 | 1223 |
1127 print "Disassembly around exception.eip:" | 1224 print "Disassembly around exception.eip:" |
1128 start = reader.ExceptionIP() - EIP_PROXIMITY | 1225 disasm_start = reader.ExceptionIP() - EIP_PROXIMITY |
1129 lines = reader.GetDisasmLines(start, 2 * EIP_PROXIMITY) | 1226 disasm_bytes = 2 * EIP_PROXIMITY |
1130 for line in lines: | 1227 if (options.full): |
1131 print FormatDisasmLine(start, heap, line) | 1228 full_range = reader.FindRegion(reader.ExceptionIP()) |
1132 print | 1229 if full_range is not None: |
1230 disasm_start = full_range[0] | |
1231 disasm_bytes = full_range[1] | |
1232 | |
1233 lines = reader.GetDisasmLines(disasm_start, disasm_bytes) | |
1234 | |
1235 for line in lines: | |
1236 print FormatDisasmLine(disasm_start, heap, line) | |
1237 print | |
1238 | |
1239 if heap is None: | |
1240 heap = V8Heap(reader, None) | |
1133 | 1241 |
1134 if options.full: | 1242 if options.full: |
1135 do_dump(reader, heap) | 1243 do_dump(reader, heap) |
1136 | 1244 |
1137 if options.shell: | 1245 if options.shell: |
1138 InspectionShell(reader, heap).cmdloop("type help to get help") | 1246 InspectionShell(reader, heap).cmdloop("type help to get help") |
1139 else: | 1247 else: |
1140 print "Annotated stack (from exception.esp to bottom):" | 1248 if reader.exception is not None: |
1141 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): | 1249 print "Annotated stack (from exception.esp to bottom):" |
1142 maybe_address = reader.ReadUIntPtr(slot) | 1250 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): |
1143 heap_object = heap.FindObject(maybe_address) | 1251 maybe_address = reader.ReadUIntPtr(slot) |
1144 print "%s: %s" % (reader.FormatIntPtr(slot), | 1252 heap_object = heap.FindObject(maybe_address) |
1145 reader.FormatIntPtr(maybe_address)) | 1253 print "%s: %s" % (reader.FormatIntPtr(slot), |
1146 if heap_object: | 1254 reader.FormatIntPtr(maybe_address)) |
1147 heap_object.Print(Printer()) | 1255 if heap_object: |
1148 print | 1256 heap_object.Print(Printer()) |
1257 print | |
1149 | 1258 |
1150 reader.Dispose() | 1259 reader.Dispose() |
1151 | 1260 |
1152 | 1261 |
1153 if __name__ == "__main__": | 1262 if __name__ == "__main__": |
1154 parser = optparse.OptionParser(USAGE) | 1263 parser = optparse.OptionParser(USAGE) |
1155 parser.add_option("-s", "--shell", dest="shell", action="store_true") | 1264 parser.add_option("-s", "--shell", dest="shell", action="store_true") |
1156 parser.add_option("-f", "--full", dest="full", action="store_true") | 1265 parser.add_option("-f", "--full", dest="full", action="store_true") |
1157 options, args = parser.parse_args() | 1266 options, args = parser.parse_args() |
1158 if len(args) != 1: | 1267 if len(args) != 1: |
1159 parser.print_help() | 1268 parser.print_help() |
1160 sys.exit(1) | 1269 sys.exit(1) |
1161 AnalyzeMinidump(options, args[0]) | 1270 AnalyzeMinidump(options, args[0]) |
OLD | NEW |