Chromium Code Reviews| 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 |