3 #----------------------------------------------------------------------
4 # This module will enable GDB remote packet logging when the
5 # 'start_gdb_log' command is called with a filename to log to. When the
6 # 'stop_gdb_log' command is called, it will disable the logging and
7 # print out statistics about how long commands took to execute and also
9 # Be sure to add the python path that points to the LLDB shared library.
11 # To use this in the embedded python interpreter using "lldb" just
12 # import it with the full path using the "command script import"
13 # command. This can be done from the LLDB command line:
14 # (lldb) command script import /path/to/gdbremote.py
15 # Or it can be added to your ~/.lldbinit file so this module is always
17 #----------------------------------------------------------------------
30 import xml.etree.ElementTree as ET
32 #----------------------------------------------------------------------
34 #----------------------------------------------------------------------
36 g_byte_order = 'little'
37 g_number_regex = re.compile('^(0x[0-9a-fA-F]+|[0-9]+)')
38 g_thread_id_regex = re.compile('^(-1|[0-9a-fA-F]+|0)')
42 '''Simple terminal colors class'''
44 def __init__(self, enabled=True):
45 # TODO: discover terminal type from "file" and disable if
46 # it can't handle the color codes
47 self.enabled = enabled
50 '''Reset all terminal colors and formatting.'''
55 def bold(self, on=True):
56 '''Enable or disable bold depending on the "on" parameter.'''
64 def italics(self, on=True):
65 '''Enable or disable italics depending on the "on" parameter.'''
73 def underline(self, on=True):
74 '''Enable or disable underline depending on the "on" parameter.'''
82 def inverse(self, on=True):
83 '''Enable or disable inverse depending on the "on" parameter.'''
91 def strike(self, on=True):
92 '''Enable or disable strike through depending on the "on" parameter.'''
100 def black(self, fg=True):
101 '''Set the foreground or background color to black.
102 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
110 def red(self, fg=True):
111 '''Set the foreground or background color to red.
112 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
120 def green(self, fg=True):
121 '''Set the foreground or background color to green.
122 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
130 def yellow(self, fg=True):
131 '''Set the foreground or background color to yellow.
132 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
140 def blue(self, fg=True):
141 '''Set the foreground or background color to blue.
142 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
150 def magenta(self, fg=True):
151 '''Set the foreground or background color to magenta.
152 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
160 def cyan(self, fg=True):
161 '''Set the foreground or background color to cyan.
162 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
170 def white(self, fg=True):
171 '''Set the foreground or background color to white.
172 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
180 def default(self, fg=True):
181 '''Set the foreground or background color to the default.
182 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
191 def start_gdb_log(debugger, command, result, dict):
192 '''Start logging GDB remote packets by enabling logging with timestamps and
193 thread safe logging. Follow a call to this function with a call to "stop_gdb_log"
194 in order to dump out the commands.'''
196 command_args = shlex.split(command)
197 usage = "usage: start_gdb_log [options] [<LOGFILEPATH>]"
198 description = '''The command enables GDB remote packet logging with timestamps. The packets will be logged to <LOGFILEPATH> if supplied, or a temporary file will be used. Logging stops when stop_gdb_log is called and the packet times will
199 be aggregated and displayed.'''
200 parser = optparse.OptionParser(
201 description=description,
202 prog='start_gdb_log',
209 help='display verbose debug info',
212 (options, args) = parser.parse_args(command_args)
218 'error: logging is already in progress with file "%s"' %
223 g_log_file = tempfile.mktemp()
228 debugger.HandleCommand(
229 'log enable --threadsafe --timestamp --file "%s" gdb-remote packets' %
232 "GDB packet logging enable with log file '%s'\nUse the 'stop_gdb_log' command to stop logging and show packet statistics." %
236 result.PutCString('error: invalid log file path')
237 result.PutCString(usage)
240 def stop_gdb_log(debugger, command, result, dict):
241 '''Stop logging GDB remote packets to the file that was specified in a call
242 to "start_gdb_log" and normalize the timestamps to be relative to the first
243 timestamp in the log file. Also print out statistics for how long each
244 command took to allow performance bottlenecks to be determined.'''
246 # Any commands whose names might be followed by more valid C identifier
247 # characters must be listed here
248 command_args = shlex.split(command)
249 usage = "usage: stop_gdb_log [options]"
250 description = '''The command stops a previously enabled GDB remote packet logging command. Packet logging must have been previously enabled with a call to start_gdb_log.'''
251 parser = optparse.OptionParser(
252 description=description,
260 help='display verbose debug info',
267 help='display verbose debug info',
274 help='add terminal colors',
281 help='display verbose debug info',
288 help='symbolicate addresses in log using current "lldb.target"',
291 (options, args) = parser.parse_args(command_args)
294 options.colors = TerminalColors(options.color)
295 options.symbolicator = None
296 if options.symbolicate:
298 import lldb.utils.symbolication
299 options.symbolicator = lldb.utils.symbolication.Symbolicator()
300 options.symbolicator.target = lldb.target
302 print "error: can't symbolicate without a target"
306 'error: logging must have been previously enabled with a call to "stop_gdb_log"')
307 elif os.path.exists(g_log_file):
309 debugger.HandleCommand('log disable gdb-remote packets')
311 "GDB packet logging disabled. Logged packets are in '%s'" %
313 parse_gdb_log_file(g_log_file, options)
315 result.PutCString(usage)
317 print 'error: the GDB packet log file "%s" does not exist' % g_log_file
320 def is_hex_byte(str):
322 return str[0] in string.hexdigits and str[1] in string.hexdigits
325 # global register info list
326 g_register_infos = list()
327 g_max_register_info_name_len = 0
331 """Class that represents register information"""
333 def __init__(self, kvp):
338 self.info[key] = value
341 '''Get the name of the register.'''
342 if self.info and 'name' in self.info:
343 return self.info['name']
347 '''Get the size in bits of the register.'''
348 if self.info and 'bitsize' in self.info:
349 return int(self.info['bitsize'])
353 '''Get the size in bytes of the register.'''
354 return self.bit_size() / 8
356 def get_value_from_hex_string(self, hex_str):
357 '''Dump the register value given a native byte order encoded hex ASCII byte string.'''
358 encoding = self.info['encoding']
359 bit_size = self.bit_size()
360 packet = Packet(hex_str)
361 if encoding == 'uint':
362 uval = packet.get_hex_uint(g_byte_order)
364 return '0x%2.2x' % (uval)
366 return '0x%4.4x' % (uval)
368 return '0x%8.8x' % (uval)
370 return '0x%16.16x' % (uval)
372 uval = packet.get_hex_uint8()
373 while uval is not None:
375 uval = packet.get_hex_uint8()
377 if g_byte_order == 'little':
380 value_str += '%2.2x' % byte
381 return '%s' % (value_str)
384 '''Dump the register info key/value pairs'''
386 for key in self.info.keys():
389 s += "%s=%s " % (key, self.info[key])
394 """Class that represents a packet that contains string data"""
396 def __init__(self, packet_str):
397 self.str = packet_str
409 self.str = self.str[1:]
412 def skip_exact_string(self, s):
413 if self.str and self.str.startswith(s):
414 self.str = self.str[len(s):]
419 def get_thread_id(self, fail_value=-1):
420 match = g_number_regex.match(self.str)
422 number_str = match.group(1)
423 self.str = self.str[len(number_str):]
424 return int(number_str, 0)
428 def get_hex_uint8(self):
429 if self.str and len(self.str) >= 2 and self.str[
430 0] in string.hexdigits and self.str[1] in string.hexdigits:
431 uval = int(self.str[0:2], 16)
432 self.str = self.str[2:]
436 def get_hex_uint16(self, byte_order):
438 if byte_order == 'big':
439 uval |= self.get_hex_uint8() << 8
440 uval |= self.get_hex_uint8()
442 uval |= self.get_hex_uint8()
443 uval |= self.get_hex_uint8() << 8
446 def get_hex_uint32(self, byte_order):
448 if byte_order == 'big':
449 uval |= self.get_hex_uint8() << 24
450 uval |= self.get_hex_uint8() << 16
451 uval |= self.get_hex_uint8() << 8
452 uval |= self.get_hex_uint8()
454 uval |= self.get_hex_uint8()
455 uval |= self.get_hex_uint8() << 8
456 uval |= self.get_hex_uint8() << 16
457 uval |= self.get_hex_uint8() << 24
460 def get_hex_uint64(self, byte_order):
462 if byte_order == 'big':
463 uval |= self.get_hex_uint8() << 56
464 uval |= self.get_hex_uint8() << 48
465 uval |= self.get_hex_uint8() << 40
466 uval |= self.get_hex_uint8() << 32
467 uval |= self.get_hex_uint8() << 24
468 uval |= self.get_hex_uint8() << 16
469 uval |= self.get_hex_uint8() << 8
470 uval |= self.get_hex_uint8()
472 uval |= self.get_hex_uint8()
473 uval |= self.get_hex_uint8() << 8
474 uval |= self.get_hex_uint8() << 16
475 uval |= self.get_hex_uint8() << 24
476 uval |= self.get_hex_uint8() << 32
477 uval |= self.get_hex_uint8() << 40
478 uval |= self.get_hex_uint8() << 48
479 uval |= self.get_hex_uint8() << 56
482 def get_number(self, fail_value=-1):
483 '''Get a number from the packet. The number must be in big endian format and should be parsed
484 according to its prefix (starts with "0x" means hex, starts with "0" means octal, starts with
485 [1-9] means decimal, etc)'''
486 match = g_number_regex.match(self.str)
488 number_str = match.group(1)
489 self.str = self.str[len(number_str):]
490 return int(number_str, 0)
494 def get_hex_ascii_str(self, n=0):
495 hex_chars = self.get_hex_chars(n)
497 return binascii.unhexlify(hex_chars)
501 def get_hex_chars(self, n=0):
502 str_len = len(self.str)
504 # n was zero, so we need to determine all hex chars and
505 # stop when we hit the end of the string of a non-hex character
506 while n < str_len and self.str[n] in string.hexdigits:
510 return None # Not enough chars
511 # Verify all chars are hex if a length was specified
513 if self.str[i] not in string.hexdigits:
514 return None # Not all hex digits
517 hex_str = self.str[0:n]
518 self.str = self.str[n:]
521 def get_hex_uint(self, byte_order, n=0):
522 if byte_order == 'big':
523 hex_str = self.get_hex_chars(n)
526 return int(hex_str, 16)
528 uval = self.get_hex_uint8()
533 while uval is not None:
534 uval_result |= (uval << shift)
536 uval = self.get_hex_uint8()
539 def get_key_value_pairs(self):
542 key_value_pairs = string.split(self.str, ';')
543 for key_value_pair in key_value_pairs:
544 if len(key_value_pair):
545 kvp.append(string.split(key_value_pair, ':'))
549 return string.split(self.str, ch)
551 def split_hex(self, ch, byte_order):
553 strings = string.split(self.str, ch)
555 hex_values.append(Packet(str).get_hex_uint(byte_order))
564 g_thread_suffix_regex = re.compile(';thread:([0-9a-fA-F]+);')
567 def get_thread_from_thread_suffix(str):
569 match = g_thread_suffix_regex.match(str)
571 return int(match.group(1), 16)
575 def cmd_qThreadStopInfo(options, cmd, args):
576 packet = Packet(args)
577 tid = packet.get_hex_uint('big')
578 print "get_thread_stop_info (tid = 0x%x)" % (tid)
581 def cmd_stop_reply(options, cmd, args):
582 print "get_last_stop_info()"
586 def rsp_stop_reply(options, cmd, cmd_args, rsp):
589 stop_type = packet.get_char()
590 if stop_type == 'T' or stop_type == 'S':
591 signo = packet.get_hex_uint8()
592 key_value_pairs = packet.get_key_value_pairs()
593 for key_value_pair in key_value_pairs:
594 key = key_value_pair[0]
596 reg_num = Packet(key).get_hex_uint8()
597 if reg_num < len(g_register_infos):
598 reg_info = g_register_infos[reg_num]
599 key_value_pair[0] = reg_info.name()
600 key_value_pair[1] = reg_info.get_value_from_hex_string(
602 elif key == 'jthreads' or key == 'jstopinfo':
603 key_value_pair[1] = binascii.unhexlify(key_value_pair[1])
604 key_value_pairs.insert(0, ['signal', signo])
605 print 'stop_reply():'
606 dump_key_value_pairs(key_value_pairs)
607 elif stop_type == 'W':
608 exit_status = packet.get_hex_uint8()
609 print 'stop_reply(): exit (status=%i)' % exit_status
610 elif stop_type == 'O':
611 print 'stop_reply(): stdout = "%s"' % packet.str
614 def cmd_unknown_packet(options, cmd, args):
616 print "cmd: %s, args: %s", cmd, args
622 def cmd_qSymbol(options, cmd, args):
624 print 'ready to serve symbols'
626 packet = Packet(args)
627 symbol_addr = packet.get_hex_uint('big')
628 if symbol_addr is None:
629 if packet.skip_exact_string(':'):
630 symbol_name = packet.get_hex_ascii_str()
631 print 'lookup_symbol("%s") -> symbol not available yet' % (symbol_name)
633 print 'error: bad command format'
635 if packet.skip_exact_string(':'):
636 symbol_name = packet.get_hex_ascii_str()
637 print 'lookup_symbol("%s") -> 0x%x' % (symbol_name, symbol_addr)
639 print 'error: bad command format'
642 def rsp_qSymbol(options, cmd, cmd_args, rsp):
647 print "No more symbols to lookup"
650 if packet.skip_exact_string("qSymbol:"):
651 symbol_name = packet.get_hex_ascii_str()
652 print 'lookup_symbol("%s")' % (symbol_name)
654 print 'error: response string should start with "qSymbol:": respnse is "%s"' % (rsp)
657 def cmd_qXfer(options, cmd, args):
658 # $qXfer:features:read:target.xml:0,1ffff#14
659 print "read target special data %s" % (args)
663 def rsp_qXfer(options, cmd, cmd_args, rsp):
664 data = string.split(cmd_args, ':')
665 if data[0] == 'features':
666 if data[1] == 'read':
667 filename, extension = os.path.splitext(data[2])
668 if extension == '.xml':
669 response = Packet(rsp)
670 xml_string = response.get_hex_ascii_str()
673 xml_string = xml_string[1:]
674 xml_root = ET.fromstring(xml_string)
675 for reg_element in xml_root.findall("./feature/reg"):
676 if not 'value_regnums' in reg_element.attrib:
677 reg_info = RegisterInfo([])
678 if 'name' in reg_element.attrib:
680 'name'] = reg_element.attrib['name']
682 reg_info.info['name'] = 'unspecified'
683 if 'encoding' in reg_element.attrib:
684 reg_info.info['encoding'] = reg_element.attrib[
687 reg_info.info['encoding'] = 'uint'
688 if 'offset' in reg_element.attrib:
690 'offset'] = reg_element.attrib['offset']
691 if 'bitsize' in reg_element.attrib:
693 'bitsize'] = reg_element.attrib['bitsize']
694 g_register_infos.append(reg_info)
695 print 'XML for "%s":' % (data[2])
699 def cmd_A(options, cmd, args):
700 print 'launch process:'
701 packet = Packet(args)
703 arg_len = packet.get_number()
706 if not packet.skip_exact_string(','):
708 arg_idx = packet.get_number()
711 if not packet.skip_exact_string(','):
713 arg_value = packet.get_hex_ascii_str(arg_len)
714 print 'argv[%u] = "%s"' % (arg_idx, arg_value)
717 def cmd_qC(options, cmd, args):
718 print "query_current_thread_id()"
721 def rsp_qC(options, cmd, cmd_args, rsp):
723 if packet.skip_exact_string("QC"):
724 tid = packet.get_thread_id()
725 print "current_thread_id = %#x" % (tid)
727 print "current_thread_id = old thread ID"
730 def cmd_query_packet(options, cmd, args):
732 print "%s%s" % (cmd, args)
738 def rsp_ok_error(rsp):
742 def rsp_ok_means_supported(options, cmd, cmd_args, rsp):
744 print "%s%s is supported" % (cmd, cmd_args)
746 print "%s%s is not supported" % (cmd, cmd_args)
748 print "%s%s -> %s" % (cmd, cmd_args, rsp)
751 def rsp_ok_means_success(options, cmd, cmd_args, rsp):
755 print "%s%s is not supported" % (cmd, cmd_args)
757 print "%s%s -> %s" % (cmd, cmd_args, rsp)
760 def dump_key_value_pairs(key_value_pairs):
762 for key_value_pair in key_value_pairs:
763 key_len = len(key_value_pair[0])
764 if max_key_len < key_len:
765 max_key_len = key_len
766 for key_value_pair in key_value_pairs:
767 key = key_value_pair[0]
768 value = key_value_pair[1]
769 print "%*s = %s" % (max_key_len, key, value)
772 def rsp_dump_key_value_pairs(options, cmd, cmd_args, rsp):
774 print '%s response:' % (cmd)
776 key_value_pairs = packet.get_key_value_pairs()
777 dump_key_value_pairs(key_value_pairs)
779 print "not supported"
782 def cmd_c(options, cmd, args):
787 def cmd_s(options, cmd, args):
792 def cmd_vCont(options, cmd, args):
794 print "%s: get supported extended continue modes" % (cmd)
796 got_other_threads = 0
798 for thread_action in string.split(args[1:], ';'):
799 (short_action, thread) = string.split(thread_action, ':')
800 tid = int(thread, 16)
801 if short_action == 'c':
803 elif short_action == 's':
805 elif short_action[0] == 'C':
806 action = 'continue with signal 0x%s' % (short_action[1:])
807 elif short_action == 'S':
808 action = 'step with signal 0x%s' % (short_action[1:])
810 action = short_action
814 got_other_threads = 1
815 s += 'other-threads:'
817 s += 'thread 0x%4.4x: %s' % (tid, action)
818 if got_other_threads:
819 print "extended_continue (%s)" % (s)
821 print "extended_continue (%s, other-threads: suspend)" % (s)
825 def rsp_vCont(options, cmd, cmd_args, rsp):
827 # Skip the leading 'vCont;'
829 modes = string.split(rsp, ';')
830 s = "%s: supported extended continue modes include: " % (cmd)
832 for i, mode in enumerate(modes):
838 s += 'continue with signal'
842 s += 'step with signal'
844 s += 'unrecognized vCont mode: ', mode
847 if rsp[0] == 'T' or rsp[0] == 'S' or rsp[0] == 'W' or rsp[0] == 'X':
848 rsp_stop_reply(options, cmd, cmd_args, rsp)
851 print "stdout: %s" % (rsp)
854 print "not supported (cmd = '%s', args = '%s', rsp = '%s')" % (cmd, cmd_args, rsp)
857 def cmd_vAttach(options, cmd, args):
858 (extra_command, args) = string.split(args, ';')
860 print "%s%s(%s)" % (cmd, extra_command, args)
862 print "attach(pid = %u)" % int(args, 16)
866 def cmd_qRegisterInfo(options, cmd, args):
867 print 'query_register_info(reg_num=%i)' % (int(args, 16))
871 def rsp_qRegisterInfo(options, cmd, cmd_args, rsp):
872 global g_max_register_info_name_len
873 print 'query_register_info(reg_num=%i):' % (int(cmd_args, 16)),
874 if len(rsp) == 3 and rsp[0] == 'E':
875 g_max_register_info_name_len = 0
876 for reg_info in g_register_infos:
877 name_len = len(reg_info.name())
878 if g_max_register_info_name_len < name_len:
879 g_max_register_info_name_len = name_len
883 reg_info = RegisterInfo(packet.get_key_value_pairs())
884 g_register_infos.append(reg_info)
889 def cmd_qThreadInfo(options, cmd, args):
890 if cmd == 'qfThreadInfo':
893 query_type = 'subsequent'
894 print 'get_current_thread_list(type=%s)' % (query_type)
898 def rsp_qThreadInfo(options, cmd, cmd_args, rsp):
900 response_type = packet.get_char()
901 if response_type == 'm':
902 tids = packet.split_hex(';', 'big')
903 for i, tid in enumerate(tids):
906 print '0x%x' % (tid),
908 elif response_type == 'l':
912 def rsp_hex_big_endian(options, cmd, cmd_args, rsp):
914 uval = packet.get_hex_uint('big')
915 print '%s: 0x%x' % (cmd, uval)
918 def cmd_read_mem_bin(options, cmd, args):
919 # x0x7fff5fc39200,0x200
920 packet = Packet(args)
921 addr = packet.get_number()
922 comma = packet.get_char()
923 size = packet.get_number()
924 print 'binary_read_memory (addr = 0x%16.16x, size = %u)' % (addr, size)
928 def rsp_mem_bin_bytes(options, cmd, cmd_args, rsp):
929 packet = Packet(cmd_args)
930 addr = packet.get_number()
931 comma = packet.get_char()
932 size = packet.get_number()
935 dump_hex_memory_buffer(addr, rsp)
938 def cmd_read_memory(options, cmd, args):
939 packet = Packet(args)
940 addr = packet.get_hex_uint('big')
941 comma = packet.get_char()
942 size = packet.get_hex_uint('big')
943 print 'read_memory (addr = 0x%16.16x, size = %u)' % (addr, size)
947 def dump_hex_memory_buffer(addr, hex_byte_str):
948 packet = Packet(hex_byte_str)
951 uval = packet.get_hex_uint8()
952 while uval is not None:
953 if ((idx % 16) == 0):
957 print '0x%x:' % (addr + idx),
958 print '%2.2x' % (uval),
959 if 0x20 <= uval and uval < 0x7f:
963 uval = packet.get_hex_uint8()
970 def cmd_write_memory(options, cmd, args):
971 packet = Packet(args)
972 addr = packet.get_hex_uint('big')
973 if packet.get_char() != ',':
974 print 'error: invalid write memory command (missing comma after address)'
976 size = packet.get_hex_uint('big')
977 if packet.get_char() != ':':
978 print 'error: invalid write memory command (missing colon after size)'
980 print 'write_memory (addr = 0x%16.16x, size = %u, data:' % (addr, size)
981 dump_hex_memory_buffer(addr, packet.str)
985 def cmd_alloc_memory(options, cmd, args):
986 packet = Packet(args)
987 byte_size = packet.get_hex_uint('big')
988 if packet.get_char() != ',':
989 print 'error: invalid allocate memory command (missing comma after address)'
991 print 'allocate_memory (byte-size = %u (0x%x), permissions = %s)' % (byte_size, byte_size, packet.str)
995 def rsp_alloc_memory(options, cmd, cmd_args, rsp):
997 addr = packet.get_hex_uint('big')
998 print 'addr = 0x%x' % addr
1001 def cmd_dealloc_memory(options, cmd, args):
1002 packet = Packet(args)
1003 addr = packet.get_hex_uint('big')
1004 if packet.get_char() != ',':
1005 print 'error: invalid allocate memory command (missing comma after address)'
1007 print 'deallocate_memory (addr = 0x%x, permissions = %s)' % (addr, packet.str)
1011 def rsp_memory_bytes(options, cmd, cmd_args, rsp):
1012 addr = Packet(cmd_args).get_hex_uint('big')
1013 dump_hex_memory_buffer(addr, rsp)
1016 def get_register_name_equal_value(options, reg_num, hex_value_str):
1017 if reg_num < len(g_register_infos):
1018 reg_info = g_register_infos[reg_num]
1019 value_str = reg_info.get_value_from_hex_string(hex_value_str)
1020 s = reg_info.name() + ' = '
1021 if options.symbolicator:
1022 symbolicated_addresses = options.symbolicator.symbolicate(
1024 if symbolicated_addresses:
1025 s += options.colors.magenta()
1026 s += '%s' % symbolicated_addresses[0]
1027 s += options.colors.reset()
1032 reg_value = Packet(hex_value_str).get_hex_uint(g_byte_order)
1033 return 'reg(%u) = 0x%x' % (reg_num, reg_value)
1036 def cmd_read_one_reg(options, cmd, args):
1037 packet = Packet(args)
1038 reg_num = packet.get_hex_uint('big')
1039 tid = get_thread_from_thread_suffix(packet.str)
1041 if reg_num < len(g_register_infos):
1042 name = g_register_infos[reg_num].name()
1044 packet.get_char() # skip ;
1045 thread_info = packet.get_key_value_pairs()
1046 tid = int(thread_info[0][1], 16)
1047 s = 'read_register (reg_num=%u' % reg_num
1049 s += ' (%s)' % (name)
1051 s += ', tid = 0x%4.4x' % (tid)
1057 def rsp_read_one_reg(options, cmd, cmd_args, rsp):
1058 packet = Packet(cmd_args)
1059 reg_num = packet.get_hex_uint('big')
1060 print get_register_name_equal_value(options, reg_num, rsp)
1063 def cmd_write_one_reg(options, cmd, args):
1064 packet = Packet(args)
1065 reg_num = packet.get_hex_uint('big')
1066 if packet.get_char() != '=':
1067 print 'error: invalid register write packet'
1070 hex_value_str = packet.get_hex_chars()
1071 tid = get_thread_from_thread_suffix(packet.str)
1072 s = 'write_register (reg_num=%u' % reg_num
1074 s += ' (%s)' % (name)
1076 s += get_register_name_equal_value(options, reg_num, hex_value_str)
1078 s += ', tid = 0x%4.4x' % (tid)
1084 def dump_all_regs(packet):
1085 for reg_info in g_register_infos:
1086 nibble_size = reg_info.bit_size() / 4
1087 hex_value_str = packet.get_hex_chars(nibble_size)
1088 if hex_value_str is not None:
1089 value = reg_info.get_value_from_hex_string(hex_value_str)
1090 print '%*s = %s' % (g_max_register_info_name_len, reg_info.name(), value)
1095 def cmd_read_all_regs(cmd, cmd_args):
1096 packet = Packet(cmd_args)
1097 packet.get_char() # toss the 'g' command character
1098 tid = get_thread_from_thread_suffix(packet.str)
1100 print 'read_all_register(thread = 0x%4.4x)' % tid
1102 print 'read_all_register()'
1106 def rsp_read_all_regs(options, cmd, cmd_args, rsp):
1107 packet = Packet(rsp)
1108 dump_all_regs(packet)
1111 def cmd_write_all_regs(options, cmd, args):
1112 packet = Packet(args)
1113 print 'write_all_registers()'
1114 dump_all_regs(packet)
1117 g_bp_types = ["software_bp", "hardware_bp", "write_wp", "read_wp", "access_wp"]
1120 def cmd_bp(options, cmd, args):
1125 packet = Packet(args)
1126 bp_type = packet.get_hex_uint('big')
1127 packet.get_char() # Skip ,
1128 bp_addr = packet.get_hex_uint('big')
1129 packet.get_char() # Skip ,
1130 bp_size = packet.get_hex_uint('big')
1131 s += g_bp_types[bp_type]
1132 s += " (addr = 0x%x, size = %u)" % (bp_addr, bp_size)
1137 def cmd_mem_rgn_info(options, cmd, args):
1138 packet = Packet(args)
1139 packet.get_char() # skip ':' character
1140 addr = packet.get_hex_uint('big')
1141 print 'get_memory_region_info (addr=0x%x)' % (addr)
1145 def cmd_kill(options, cmd, args):
1146 print 'kill_process()'
1150 def cmd_jThreadsInfo(options, cmd, args):
1151 print 'jThreadsInfo()'
1155 def cmd_jGetLoadedDynamicLibrariesInfos(options, cmd, args):
1156 print 'jGetLoadedDynamicLibrariesInfos()'
1160 def decode_packet(s, start_index=0):
1161 # print '\ndecode_packet("%s")' % (s[start_index:])
1162 index = s.find('}', start_index)
1163 have_escapes = index != -1
1165 normal_s = s[start_index:index]
1167 normal_s = s[start_index:]
1168 # print 'normal_s = "%s"' % (normal_s)
1170 escape_char = '%c' % (ord(s[index + 1]) ^ 0x20)
1171 # print 'escape_char for "%s" = %c' % (s[index:index+2], escape_char)
1172 return normal_s + escape_char + decode_packet(s, index + 2)
1177 def rsp_json(options, cmd, cmd_args, rsp):
1178 print '%s() reply:' % (cmd)
1179 json_tree = json.loads(rsp)
1180 print json.dumps(json_tree, indent=4, separators=(',', ': '))
1183 def rsp_jGetLoadedDynamicLibrariesInfos(options, cmd, cmd_args, rsp):
1185 rsp_json(options, cmd, cmd_args, rsp)
1187 rsp_ok_means_supported(options, cmd, cmd_args, rsp)
1189 gdb_remote_commands = {
1190 '\\?': {'cmd': cmd_stop_reply, 'rsp': rsp_stop_reply, 'name': "stop reply pacpket"},
1191 'qThreadStopInfo': {'cmd': cmd_qThreadStopInfo, 'rsp': rsp_stop_reply, 'name': "stop reply pacpket"},
1192 'QStartNoAckMode': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query if no ack mode is supported"},
1193 'QThreadSuffixSupported': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query if thread suffix is supported"},
1194 'QListThreadsInStopReply': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query if threads in stop reply packets are supported"},
1195 'QSetDetachOnError': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_success, 'name': "set if we should detach on error"},
1196 'QSetDisableASLR': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_success, 'name': "set if we should disable ASLR"},
1197 'qLaunchSuccess': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_success, 'name': "check on launch success for the A packet"},
1198 'A': {'cmd': cmd_A, 'rsp': rsp_ok_means_success, 'name': "launch process"},
1199 'QLaunchArch': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "set if we should disable ASLR"},
1200 'qVAttachOrWaitSupported': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "set the launch architecture"},
1201 'qHostInfo': {'cmd': cmd_query_packet, 'rsp': rsp_dump_key_value_pairs, 'name': "get host information"},
1202 'qC': {'cmd': cmd_qC, 'rsp': rsp_qC, 'name': "return the current thread ID"},
1203 'vCont': {'cmd': cmd_vCont, 'rsp': rsp_vCont, 'name': "extended continue command"},
1204 'vAttach': {'cmd': cmd_vAttach, 'rsp': rsp_stop_reply, 'name': "attach to process"},
1205 'c': {'cmd': cmd_c, 'rsp': rsp_stop_reply, 'name': "continue"},
1206 's': {'cmd': cmd_s, 'rsp': rsp_stop_reply, 'name': "step"},
1207 'qRegisterInfo': {'cmd': cmd_qRegisterInfo, 'rsp': rsp_qRegisterInfo, 'name': "query register info"},
1208 'qfThreadInfo': {'cmd': cmd_qThreadInfo, 'rsp': rsp_qThreadInfo, 'name': "get current thread list"},
1209 'qsThreadInfo': {'cmd': cmd_qThreadInfo, 'rsp': rsp_qThreadInfo, 'name': "get current thread list"},
1210 'qShlibInfoAddr': {'cmd': cmd_query_packet, 'rsp': rsp_hex_big_endian, 'name': "get shared library info address"},
1211 'qMemoryRegionInfo': {'cmd': cmd_mem_rgn_info, 'rsp': rsp_dump_key_value_pairs, 'name': "get memory region information"},
1212 'qProcessInfo': {'cmd': cmd_query_packet, 'rsp': rsp_dump_key_value_pairs, 'name': "get process info"},
1213 'qSupported': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query supported"},
1214 'qXfer:': {'cmd': cmd_qXfer, 'rsp': rsp_qXfer, 'name': "qXfer"},
1215 'qSymbol:': {'cmd': cmd_qSymbol, 'rsp': rsp_qSymbol, 'name': "qSymbol"},
1216 'x': {'cmd': cmd_read_mem_bin, 'rsp': rsp_mem_bin_bytes, 'name': "read memory binary"},
1217 'X': {'cmd': cmd_write_memory, 'rsp': rsp_ok_means_success, 'name': "write memory binary"},
1218 'm': {'cmd': cmd_read_memory, 'rsp': rsp_memory_bytes, 'name': "read memory"},
1219 'M': {'cmd': cmd_write_memory, 'rsp': rsp_ok_means_success, 'name': "write memory"},
1220 '_M': {'cmd': cmd_alloc_memory, 'rsp': rsp_alloc_memory, 'name': "allocate memory"},
1221 '_m': {'cmd': cmd_dealloc_memory, 'rsp': rsp_ok_means_success, 'name': "deallocate memory"},
1222 'p': {'cmd': cmd_read_one_reg, 'rsp': rsp_read_one_reg, 'name': "read single register"},
1223 'P': {'cmd': cmd_write_one_reg, 'rsp': rsp_ok_means_success, 'name': "write single register"},
1224 'g': {'cmd': cmd_read_all_regs, 'rsp': rsp_read_all_regs, 'name': "read all registers"},
1225 'G': {'cmd': cmd_write_all_regs, 'rsp': rsp_ok_means_success, 'name': "write all registers"},
1226 'z': {'cmd': cmd_bp, 'rsp': rsp_ok_means_success, 'name': "clear breakpoint or watchpoint"},
1227 'Z': {'cmd': cmd_bp, 'rsp': rsp_ok_means_success, 'name': "set breakpoint or watchpoint"},
1228 'k': {'cmd': cmd_kill, 'rsp': rsp_stop_reply, 'name': "kill process"},
1229 'jThreadsInfo': {'cmd': cmd_jThreadsInfo, 'rsp': rsp_json, 'name': "JSON get all threads info"},
1230 'jGetLoadedDynamicLibrariesInfos:': {'cmd': cmd_jGetLoadedDynamicLibrariesInfos, 'rsp': rsp_jGetLoadedDynamicLibrariesInfos, 'name': 'JSON get loaded dynamic libraries'},
1234 def calculate_mean_and_standard_deviation(floats):
1245 accum += delta * delta
1247 std_dev = math.sqrt(accum / (count - 1))
1248 return (mean, std_dev)
1251 def parse_gdb_log_file(path, options):
1253 parse_gdb_log(f, options)
1257 def parse_gdb_log(file, options):
1258 '''Parse a GDB log file that was generated by enabling logging with:
1259 (lldb) log enable --threadsafe --timestamp --file <FILE> gdb-remote packets
1260 This log file will contain timestamps and this function will then normalize
1261 those packets to be relative to the first value timestamp that is found and
1262 show delta times between log lines and also keep track of how long it takes
1263 for GDB remote commands to make a send/receive round trip. This can be
1264 handy when trying to figure out why some operation in the debugger is taking
1265 a long time during a preset set of debugger commands.'''
1267 tricky_commands = ['qRegisterInfo']
1268 timestamp_regex = re.compile('(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$')
1269 packet_name_regex = re.compile('([A-Za-z_]+)[^a-z]')
1270 packet_transmit_name_regex = re.compile(
1271 '(?P<direction>send|read) packet: (?P<packet>.*)')
1272 packet_contents_name_regex = re.compile('\$([^#]+)#[0-9a-fA-F]{2}')
1273 packet_checksum_regex = re.compile('.*#[0-9a-fA-F]{2}$')
1274 packet_names_regex_str = '(' + \
1275 '|'.join(gdb_remote_commands.keys()) + ')(.*)'
1276 packet_names_regex = re.compile(packet_names_regex_str)
1280 packet_send_time = 0.0
1281 packet_total_times = {}
1284 lines = file.read().splitlines()
1286 last_command_args = None
1287 last_command_packet = None
1288 hide_next_response = False
1289 num_lines = len(lines)
1291 for (line_index, line) in enumerate(lines):
1292 # See if we need to skip any lines
1296 m = packet_transmit_name_regex.search(line)
1300 direction = m.group('direction')
1301 is_command = direction == 'send'
1302 packet = m.group('packet')
1303 sys.stdout.write(options.colors.green())
1304 if not options.quiet and not hide_next_response:
1306 sys.stdout.write(options.colors.reset())
1308 # print 'direction = "%s", packet = "%s"' % (direction, packet)
1310 if packet[0] == '+':
1315 if not options.quiet:
1318 elif packet[0] == '-':
1323 if not options.quiet:
1326 elif packet[0] == '$':
1327 m = packet_contents_name_regex.match(packet)
1328 if not m and packet[0] == '$':
1329 multiline_packet = packet
1330 idx = line_index + 1
1331 while idx < num_lines:
1332 if not options.quiet and not hide_next_response:
1333 print '# ', lines[idx]
1334 multiline_packet += lines[idx]
1335 m = packet_contents_name_regex.match(multiline_packet)
1337 packet = multiline_packet
1338 skip_count = idx - line_index
1347 contents = decode_packet(m.group(1))
1349 hide_next_response = False
1350 m = packet_names_regex.match(contents)
1352 last_command = m.group(1)
1353 if last_command == '?':
1354 last_command = '\\?'
1355 packet_name = last_command
1356 last_command_args = m.group(2)
1357 last_command_packet = contents
1358 hide_next_response = gdb_remote_commands[last_command][
1359 'cmd'](options, last_command, last_command_args)
1361 packet_match = packet_name_regex.match(contents)
1363 packet_name = packet_match.group(1)
1364 for tricky_cmd in tricky_commands:
1365 if packet_name.find(tricky_cmd) == 0:
1366 packet_name = tricky_cmd
1368 packet_name = contents
1370 last_command_args = None
1371 last_command_packet = None
1373 gdb_remote_commands[last_command]['rsp'](
1374 options, last_command, last_command_args, contents)
1376 print 'error: invalid packet: "', packet, '"'
1381 match = timestamp_regex.match(line)
1383 curr_time = float(match.group(2))
1384 if last_time and not is_command:
1385 delta = curr_time - last_time
1386 packet_times.append(delta)
1389 delta = curr_time - last_time
1391 base_time = curr_time
1394 packet_send_time = curr_time
1395 elif line.find('read packet: $') >= 0 and packet_name:
1396 if packet_name in packet_total_times:
1397 packet_total_times[packet_name] += delta
1398 packet_count[packet_name] += 1
1400 packet_total_times[packet_name] = delta
1401 packet_count[packet_name] = 1
1404 if not options or not options.quiet:
1405 print '%s%.6f %+.6f%s' % (match.group(1), curr_time - base_time, delta, match.group(3))
1406 last_time = curr_time
1409 (average, std_dev) = calculate_mean_and_standard_deviation(packet_times)
1410 if average and std_dev:
1411 print '%u packets with average packet time of %f and standard deviation of %f' % (len(packet_times), average, std_dev)
1412 if packet_total_times:
1413 total_packet_time = 0.0
1414 total_packet_count = 0
1415 for key, vvv in packet_total_times.items():
1416 # print ' key = (%s) "%s"' % (type(key), key)
1417 # print 'value = (%s) %s' % (type(vvv), vvv)
1418 # if type(vvv) == 'float':
1419 total_packet_time += vvv
1420 for key, vvv in packet_count.items():
1421 total_packet_count += vvv
1423 print '#---------------------------------------------------'
1424 print '# Packet timing summary:'
1425 print '# Totals: time = %6f, count = %6d' % (total_packet_time, total_packet_count)
1426 print '#---------------------------------------------------'
1427 print '# Packet Time (sec) Percent Count '
1428 print '#------------------------- ---------- ------- ------'
1429 if options and options.sort_count:
1432 key=packet_count.__getitem__,
1437 key=packet_total_times.__getitem__,
1442 packet_total_time = packet_total_times[item]
1444 packet_total_time / total_packet_time) * 100.0
1445 if packet_percent >= 10.0:
1446 print " %24s %.6f %.2f%% %6d" % (item, packet_total_time, packet_percent, packet_count[item])
1448 print " %24s %.6f %.2f%% %6d" % (item, packet_total_time, packet_percent, packet_count[item])
1451 if __name__ == '__main__':
1452 usage = "usage: gdbremote [options]"
1453 description = '''The command disassembles a GDB remote packet log.'''
1454 parser = optparse.OptionParser(
1455 description=description,
1461 action='store_true',
1463 help='display verbose debug info',
1468 action='store_true',
1470 help='display verbose debug info',
1475 action='store_true',
1477 help='add terminal colors',
1482 action='store_true',
1484 help='display verbose debug info',
1490 help='symbolicate using a darwin crash log file',
1493 (options, args) = parser.parse_args(sys.argv[1:])
1495 print 'error: argument error'
1498 options.colors = TerminalColors(options.color)
1499 options.symbolicator = None
1500 if options.crashlog:
1502 lldb.debugger = lldb.SBDebugger.Create()
1503 import lldb.macosx.crashlog
1504 options.symbolicator = lldb.macosx.crashlog.CrashLog(options.crashlog)
1505 print '%s' % (options.symbolicator)
1507 # This script is being run from the command line, create a debugger in case we are
1508 # going to use any debugger functions in our function.
1511 print '#----------------------------------------------------------------------'
1512 print "# GDB remote log file: '%s'" % file
1513 print '#----------------------------------------------------------------------'
1514 parse_gdb_log_file(file, options)
1515 if options.symbolicator:
1516 print '%s' % (options.symbolicator)
1518 parse_gdb_log(sys.stdin, options)
1523 # This initializer is being run from LLDB in the embedded command interpreter
1524 # Add any commands contained in this module to LLDB
1525 lldb.debugger.HandleCommand(
1526 'command script add -f gdbremote.start_gdb_log start_gdb_log')
1527 lldb.debugger.HandleCommand(
1528 'command script add -f gdbremote.stop_gdb_log stop_gdb_log')
1529 print 'The "start_gdb_log" and "stop_gdb_log" commands are now installed and ready for use, type "start_gdb_log --help" or "stop_gdb_log --help" for more information'