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',
266 help='plot packet latencies by packet type',
273 help='display verbose debug info',
280 help='add terminal colors',
287 help='display verbose debug info',
294 help='symbolicate addresses in log using current "lldb.target"',
297 (options
, args
) = parser
.parse_args(command_args
)
300 options
.colors
= TerminalColors(options
.color
)
301 options
.symbolicator
= None
302 if options
.symbolicate
:
304 import lldb
.utils
.symbolication
305 options
.symbolicator
= lldb
.utils
.symbolication
.Symbolicator()
306 options
.symbolicator
.target
= lldb
.target
308 print("error: can't symbolicate without a target")
312 'error: logging must have been previously enabled with a call to "stop_gdb_log"')
313 elif os
.path
.exists(g_log_file
):
315 debugger
.HandleCommand('log disable gdb-remote packets')
317 "GDB packet logging disabled. Logged packets are in '%s'" %
319 parse_gdb_log_file(g_log_file
, options
)
321 result
.PutCString(usage
)
323 print('error: the GDB packet log file "%s" does not exist' % g_log_file
)
326 def is_hex_byte(str):
328 return str[0] in string
.hexdigits
and str[1] in string
.hexdigits
331 def get_hex_string_if_all_printable(str):
333 s
= binascii
.unhexlify(str).decode()
334 if all(c
in string
.printable
for c
in s
):
336 except (TypeError, binascii
.Error
, UnicodeDecodeError):
340 # global register info list
341 g_register_infos
= list()
342 g_max_register_info_name_len
= 0
346 """Class that represents register information"""
348 def __init__(self
, kvp
):
353 self
.info
[key
] = value
356 '''Get the name of the register.'''
357 if self
.info
and 'name' in self
.info
:
358 return self
.info
['name']
362 '''Get the size in bits of the register.'''
363 if self
.info
and 'bitsize' in self
.info
:
364 return int(self
.info
['bitsize'])
368 '''Get the size in bytes of the register.'''
369 return self
.bit_size() / 8
371 def get_value_from_hex_string(self
, hex_str
):
372 '''Dump the register value given a native byte order encoded hex ASCII byte string.'''
373 encoding
= self
.info
['encoding']
374 bit_size
= self
.bit_size()
375 packet
= Packet(hex_str
)
376 if encoding
== 'uint':
377 uval
= packet
.get_hex_uint(g_byte_order
)
379 return '0x%2.2x' % (uval
)
381 return '0x%4.4x' % (uval
)
383 return '0x%8.8x' % (uval
)
385 return '0x%16.16x' % (uval
)
387 uval
= packet
.get_hex_uint8()
388 while uval
is not None:
390 uval
= packet
.get_hex_uint8()
392 if g_byte_order
== 'little':
395 value_str
+= '%2.2x' % byte
396 return '%s' % (value_str
)
399 '''Dump the register info key/value pairs'''
401 for key
in self
.info
.keys():
404 s
+= "%s=%s " % (key
, self
.info
[key
])
409 """Class that represents a packet that contains string data"""
411 def __init__(self
, packet_str
):
412 self
.str = packet_str
424 self
.str = self
.str[1:]
427 def skip_exact_string(self
, s
):
428 if self
.str and self
.str.startswith(s
):
429 self
.str = self
.str[len(s
):]
434 def get_thread_id(self
, fail_value
=-1):
435 match
= g_number_regex
.match(self
.str)
437 number_str
= match
.group(1)
438 self
.str = self
.str[len(number_str
):]
439 return int(number_str
, 0)
443 def get_hex_uint8(self
):
444 if self
.str and len(self
.str) >= 2 and self
.str[
445 0] in string
.hexdigits
and self
.str[1] in string
.hexdigits
:
446 uval
= int(self
.str[0:2], 16)
447 self
.str = self
.str[2:]
451 def get_hex_uint16(self
, byte_order
):
453 if byte_order
== 'big':
454 uval |
= self
.get_hex_uint8() << 8
455 uval |
= self
.get_hex_uint8()
457 uval |
= self
.get_hex_uint8()
458 uval |
= self
.get_hex_uint8() << 8
461 def get_hex_uint32(self
, byte_order
):
463 if byte_order
== 'big':
464 uval |
= self
.get_hex_uint8() << 24
465 uval |
= self
.get_hex_uint8() << 16
466 uval |
= self
.get_hex_uint8() << 8
467 uval |
= self
.get_hex_uint8()
469 uval |
= self
.get_hex_uint8()
470 uval |
= self
.get_hex_uint8() << 8
471 uval |
= self
.get_hex_uint8() << 16
472 uval |
= self
.get_hex_uint8() << 24
475 def get_hex_uint64(self
, byte_order
):
477 if byte_order
== 'big':
478 uval |
= self
.get_hex_uint8() << 56
479 uval |
= self
.get_hex_uint8() << 48
480 uval |
= self
.get_hex_uint8() << 40
481 uval |
= self
.get_hex_uint8() << 32
482 uval |
= self
.get_hex_uint8() << 24
483 uval |
= self
.get_hex_uint8() << 16
484 uval |
= self
.get_hex_uint8() << 8
485 uval |
= self
.get_hex_uint8()
487 uval |
= self
.get_hex_uint8()
488 uval |
= self
.get_hex_uint8() << 8
489 uval |
= self
.get_hex_uint8() << 16
490 uval |
= self
.get_hex_uint8() << 24
491 uval |
= self
.get_hex_uint8() << 32
492 uval |
= self
.get_hex_uint8() << 40
493 uval |
= self
.get_hex_uint8() << 48
494 uval |
= self
.get_hex_uint8() << 56
497 def get_number(self
, fail_value
=-1):
498 '''Get a number from the packet. The number must be in big endian format and should be parsed
499 according to its prefix (starts with "0x" means hex, starts with "0" means octal, starts with
500 [1-9] means decimal, etc)'''
501 match
= g_number_regex
.match(self
.str)
503 number_str
= match
.group(1)
504 self
.str = self
.str[len(number_str
):]
505 return int(number_str
, 0)
509 def get_hex_ascii_str(self
, n
=0):
510 hex_chars
= self
.get_hex_chars(n
)
512 return binascii
.unhexlify(hex_chars
)
516 def get_hex_chars(self
, n
=0):
517 str_len
= len(self
.str)
519 # n was zero, so we need to determine all hex chars and
520 # stop when we hit the end of the string of a non-hex character
521 while n
< str_len
and self
.str[n
] in string
.hexdigits
:
525 return None # Not enough chars
526 # Verify all chars are hex if a length was specified
528 if self
.str[i
] not in string
.hexdigits
:
529 return None # Not all hex digits
532 hex_str
= self
.str[0:n
]
533 self
.str = self
.str[n
:]
536 def get_hex_uint(self
, byte_order
, n
=0):
537 if byte_order
== 'big':
538 hex_str
= self
.get_hex_chars(n
)
541 return int(hex_str
, 16)
543 uval
= self
.get_hex_uint8()
548 while uval
is not None:
549 uval_result |
= (uval
<< shift
)
551 uval
= self
.get_hex_uint8()
554 def get_key_value_pairs(self
):
557 key_value_pairs
= self
.str.split(';')
558 for key_value_pair
in key_value_pairs
:
559 if len(key_value_pair
):
560 kvp
.append(key_value_pair
.split(':', 1))
564 return self
.str.split(ch
)
566 def split_hex(self
, ch
, byte_order
):
568 strings
= self
.str.split(ch
)
570 hex_values
.append(Packet(str).get_hex_uint(byte_order
))
579 g_thread_suffix_regex
= re
.compile(';thread:([0-9a-fA-F]+);')
582 def get_thread_from_thread_suffix(str):
584 match
= g_thread_suffix_regex
.match(str)
586 return int(match
.group(1), 16)
590 def cmd_qThreadStopInfo(options
, cmd
, args
):
591 packet
= Packet(args
)
592 tid
= packet
.get_hex_uint('big')
593 print("get_thread_stop_info (tid = 0x%x)" % (tid
))
596 def cmd_stop_reply(options
, cmd
, args
):
597 print("get_last_stop_info()")
601 def rsp_stop_reply(options
, cmd
, cmd_args
, rsp
):
604 stop_type
= packet
.get_char()
605 if stop_type
== 'T' or stop_type
== 'S':
606 signo
= packet
.get_hex_uint8()
607 key_value_pairs
= packet
.get_key_value_pairs()
608 for key_value_pair
in key_value_pairs
:
609 key
= key_value_pair
[0]
611 reg_num
= Packet(key
).get_hex_uint8()
612 if reg_num
< len(g_register_infos
):
613 reg_info
= g_register_infos
[reg_num
]
614 key_value_pair
[0] = reg_info
.name()
615 key_value_pair
[1] = reg_info
.get_value_from_hex_string(
617 elif key
== 'jthreads' or key
== 'jstopinfo':
618 key_value_pair
[1] = binascii
.unhexlify(key_value_pair
[1])
619 key_value_pairs
.insert(0, ['signal', signo
])
620 print('stop_reply():')
621 dump_key_value_pairs(key_value_pairs
)
622 elif stop_type
== 'W':
623 exit_status
= packet
.get_hex_uint8()
624 print('stop_reply(): exit (status=%i)' % exit_status
)
625 elif stop_type
== 'O':
626 print('stop_reply(): stdout = "%s"' % packet
.str)
629 def cmd_unknown_packet(options
, cmd
, args
):
631 print("cmd: %s, args: %s", cmd
, args
)
633 print("cmd: %s", cmd
)
637 def cmd_qSymbol(options
, cmd
, args
):
639 print('ready to serve symbols')
641 packet
= Packet(args
)
642 symbol_addr
= packet
.get_hex_uint('big')
643 if symbol_addr
is None:
644 if packet
.skip_exact_string(':'):
645 symbol_name
= packet
.get_hex_ascii_str()
646 print('lookup_symbol("%s") -> symbol not available yet' % (symbol_name
))
648 print('error: bad command format')
650 if packet
.skip_exact_string(':'):
651 symbol_name
= packet
.get_hex_ascii_str()
652 print('lookup_symbol("%s") -> 0x%x' % (symbol_name
, symbol_addr
))
654 print('error: bad command format')
656 def cmd_QSetWithHexString(options
, cmd
, args
):
657 print('%s("%s")' % (cmd
[:-1], binascii
.unhexlify(args
)))
659 def cmd_QSetWithString(options
, cmd
, args
):
660 print('%s("%s")' % (cmd
[:-1], args
))
662 def cmd_QSetWithUnsigned(options
, cmd
, args
):
663 print('%s(%i)' % (cmd
[:-1], int(args
)))
665 def rsp_qSymbol(options
, cmd
, cmd_args
, rsp
):
670 print("No more symbols to lookup")
673 if packet
.skip_exact_string("qSymbol:"):
674 symbol_name
= packet
.get_hex_ascii_str()
675 print('lookup_symbol("%s")' % (symbol_name
))
677 print('error: response string should start with "qSymbol:": respnse is "%s"' % (rsp
))
680 def cmd_qXfer(options
, cmd
, args
):
681 # $qXfer:features:read:target.xml:0,1ffff#14
682 print("read target special data %s" % (args
))
686 def rsp_qXfer(options
, cmd
, cmd_args
, rsp
):
687 data
= cmd_args
.split(':')
688 if data
[0] == 'features':
689 if data
[1] == 'read':
690 filename
, extension
= os
.path
.splitext(data
[2])
691 if extension
== '.xml':
692 response
= Packet(rsp
)
693 xml_string
= response
.get_hex_ascii_str()
697 xml_string
= xml_string
[1:]
698 xml_root
= ET
.fromstring(xml_string
)
699 for reg_element
in xml_root
.findall("./feature/reg"):
700 if not 'value_regnums' in reg_element
.attrib
:
701 reg_info
= RegisterInfo([])
702 if 'name' in reg_element
.attrib
:
704 'name'] = reg_element
.attrib
['name']
706 reg_info
.info
['name'] = 'unspecified'
707 if 'encoding' in reg_element
.attrib
:
708 reg_info
.info
['encoding'] = reg_element
.attrib
[
711 reg_info
.info
['encoding'] = 'uint'
712 if 'offset' in reg_element
.attrib
:
714 'offset'] = reg_element
.attrib
['offset']
715 if 'bitsize' in reg_element
.attrib
:
717 'bitsize'] = reg_element
.attrib
['bitsize']
718 g_register_infos
.append(reg_info
)
719 print('XML for "%s":' % (data
[2]))
723 def cmd_A(options
, cmd
, args
):
724 print('launch process:')
725 packet
= Packet(args
)
727 arg_len
= packet
.get_number()
730 if not packet
.skip_exact_string(','):
732 arg_idx
= packet
.get_number()
735 if not packet
.skip_exact_string(','):
737 arg_value
= packet
.get_hex_ascii_str(arg_len
)
738 print('argv[%u] = "%s"' % (arg_idx
, arg_value
))
741 def cmd_qC(options
, cmd
, args
):
742 print("query_current_thread_id()")
745 def rsp_qC(options
, cmd
, cmd_args
, rsp
):
747 if packet
.skip_exact_string("QC"):
748 tid
= packet
.get_thread_id()
749 print("current_thread_id = %#x" % (tid
))
751 print("current_thread_id = old thread ID")
754 def cmd_query_packet(options
, cmd
, args
):
756 print("%s%s" % (cmd
, args
))
762 def rsp_ok_error(rsp
):
766 def rsp_ok_means_supported(options
, cmd
, cmd_args
, rsp
):
768 print("%s%s is supported" % (cmd
, cmd_args
))
770 print("%s%s is not supported" % (cmd
, cmd_args
))
772 print("%s%s -> %s" % (cmd
, cmd_args
, rsp
))
775 def rsp_ok_means_success(options
, cmd
, cmd_args
, rsp
):
779 print("%s%s is not supported" % (cmd
, cmd_args
))
781 print("%s%s -> %s" % (cmd
, cmd_args
, rsp
))
784 def dump_key_value_pairs(key_value_pairs
):
786 for key_value_pair
in key_value_pairs
:
787 key_len
= len(key_value_pair
[0])
788 if max_key_len
< key_len
:
789 max_key_len
= key_len
790 for key_value_pair
in key_value_pairs
:
791 key
= key_value_pair
[0]
792 value
= key_value_pair
[1]
793 unhex_value
= get_hex_string_if_all_printable(value
)
795 print("%*s = %s (%s)" % (max_key_len
, key
, value
, unhex_value
))
797 print("%*s = %s" % (max_key_len
, key
, value
))
800 def rsp_dump_key_value_pairs(options
, cmd
, cmd_args
, rsp
):
802 print('%s response:' % (cmd
))
804 key_value_pairs
= packet
.get_key_value_pairs()
805 dump_key_value_pairs(key_value_pairs
)
807 print("not supported")
810 def cmd_c(options
, cmd
, args
):
815 def cmd_s(options
, cmd
, args
):
820 def cmd_qSpeedTest(options
, cmd
, args
):
821 print(("qSpeedTest: cmd='%s', args='%s'" % (cmd
, args
)))
824 def rsp_qSpeedTest(options
, cmd
, cmd_args
, rsp
):
825 print(("qSpeedTest: rsp='%s' cmd='%s', args='%s'" % (rsp
, cmd
, args
)))
828 def cmd_vCont(options
, cmd
, args
):
830 print("%s: get supported extended continue modes" % (cmd
))
832 got_other_threads
= 0
834 for thread_action
in args
[1:].split(';'):
835 (short_action
, thread
) = thread_action
.split(':', 1)
836 tid
= int(thread
, 16)
837 if short_action
== 'c':
839 elif short_action
== 's':
841 elif short_action
[0] == 'C':
842 action
= 'continue with signal 0x%s' % (short_action
[1:])
843 elif short_action
== 'S':
844 action
= 'step with signal 0x%s' % (short_action
[1:])
846 action
= short_action
850 got_other_threads
= 1
851 s
+= 'other-threads:'
853 s
+= 'thread 0x%4.4x: %s' % (tid
, action
)
854 if got_other_threads
:
855 print("extended_continue (%s)" % (s
))
857 print("extended_continue (%s, other-threads: suspend)" % (s
))
861 def rsp_vCont(options
, cmd
, cmd_args
, rsp
):
863 # Skip the leading 'vCont;'
865 modes
= rsp
.split(';')
866 s
= "%s: supported extended continue modes include: " % (cmd
)
868 for i
, mode
in enumerate(modes
):
874 s
+= 'continue with signal'
878 s
+= 'step with signal'
882 # s += 'unrecognized vCont mode: ', str(mode)
885 if rsp
[0] == 'T' or rsp
[0] == 'S' or rsp
[0] == 'W' or rsp
[0] == 'X':
886 rsp_stop_reply(options
, cmd
, cmd_args
, rsp
)
889 print("stdout: %s" % (rsp
))
892 print("not supported (cmd = '%s', args = '%s', rsp = '%s')" % (cmd
, cmd_args
, rsp
))
895 def cmd_vAttach(options
, cmd
, args
):
896 (extra_command
, args
) = args
.split(';')
898 print("%s%s(%s)" % (cmd
, extra_command
, args
))
900 print("attach(pid = %u)" % int(args
, 16))
904 def cmd_qRegisterInfo(options
, cmd
, args
):
905 print('query_register_info(reg_num=%i)' % (int(args
, 16)))
909 def rsp_qRegisterInfo(options
, cmd
, cmd_args
, rsp
):
910 global g_max_register_info_name_len
911 print('query_register_info(reg_num=%i):' % (int(cmd_args
, 16)), end
=' ')
912 if len(rsp
) == 3 and rsp
[0] == 'E':
913 g_max_register_info_name_len
= 0
914 for reg_info
in g_register_infos
:
915 name_len
= len(reg_info
.name())
916 if g_max_register_info_name_len
< name_len
:
917 g_max_register_info_name_len
= name_len
921 reg_info
= RegisterInfo(packet
.get_key_value_pairs())
922 g_register_infos
.append(reg_info
)
927 def cmd_qThreadInfo(options
, cmd
, args
):
928 if cmd
== 'qfThreadInfo':
931 query_type
= 'subsequent'
932 print('get_current_thread_list(type=%s)' % (query_type
))
936 def rsp_qThreadInfo(options
, cmd
, cmd_args
, rsp
):
938 response_type
= packet
.get_char()
939 if response_type
== 'm':
940 tids
= packet
.split_hex(';', 'big')
941 for i
, tid
in enumerate(tids
):
944 print('0x%x' % (tid
), end
=' ')
946 elif response_type
== 'l':
950 def rsp_hex_big_endian(options
, cmd
, cmd_args
, rsp
):
952 print("%s%s is not supported" % (cmd
, cmd_args
))
955 uval
= packet
.get_hex_uint('big')
956 print('%s: 0x%x' % (cmd
, uval
))
959 def cmd_read_mem_bin(options
, cmd
, args
):
960 # x0x7fff5fc39200,0x200
961 packet
= Packet(args
)
962 addr
= packet
.get_hex_uint('big')
963 comma
= packet
.get_char()
964 size
= packet
.get_hex_uint('big')
965 print('binary_read_memory (addr = 0x%16.16x, size = %u)' % (addr
, size
))
969 def rsp_mem_bin_bytes(options
, cmd
, cmd_args
, rsp
):
970 packet
= Packet(cmd_args
)
971 addr
= packet
.get_hex_uint('big')
972 comma
= packet
.get_char()
973 size
= packet
.get_hex_uint('big')
976 dump_hex_memory_buffer(addr
, rsp
)
979 def cmd_read_memory(options
, cmd
, args
):
980 packet
= Packet(args
)
981 addr
= packet
.get_hex_uint('big')
982 comma
= packet
.get_char()
983 size
= packet
.get_hex_uint('big')
984 print('read_memory (addr = 0x%16.16x, size = %u)' % (addr
, size
))
988 def dump_hex_memory_buffer(addr
, hex_byte_str
):
989 packet
= Packet(hex_byte_str
)
992 uval
= packet
.get_hex_uint8()
993 while uval
is not None:
994 if ((idx
% 16) == 0):
998 print('0x%x:' % (addr
+ idx
), end
=' ')
999 print('%2.2x' % (uval
), end
=' ')
1000 if 0x20 <= uval
and uval
< 0x7f:
1001 ascii
+= '%c' % uval
1004 uval
= packet
.get_hex_uint8()
1011 def cmd_write_memory(options
, cmd
, args
):
1012 packet
= Packet(args
)
1013 addr
= packet
.get_hex_uint('big')
1014 if packet
.get_char() != ',':
1015 print('error: invalid write memory command (missing comma after address)')
1017 size
= packet
.get_hex_uint('big')
1018 if packet
.get_char() != ':':
1019 print('error: invalid write memory command (missing colon after size)')
1021 print('write_memory (addr = 0x%16.16x, size = %u, data:' % (addr
, size
))
1022 dump_hex_memory_buffer(addr
, packet
.str)
1026 def cmd_alloc_memory(options
, cmd
, args
):
1027 packet
= Packet(args
)
1028 byte_size
= packet
.get_hex_uint('big')
1029 if packet
.get_char() != ',':
1030 print('error: invalid allocate memory command (missing comma after address)')
1032 print('allocate_memory (byte-size = %u (0x%x), permissions = %s)' % (byte_size
, byte_size
, packet
.str))
1036 def rsp_alloc_memory(options
, cmd
, cmd_args
, rsp
):
1037 packet
= Packet(rsp
)
1038 addr
= packet
.get_hex_uint('big')
1039 print('addr = 0x%x' % addr
)
1042 def cmd_dealloc_memory(options
, cmd
, args
):
1043 packet
= Packet(args
)
1044 addr
= packet
.get_hex_uint('big')
1045 if packet
.get_char() != ',':
1046 print('error: invalid allocate memory command (missing comma after address)')
1048 print('deallocate_memory (addr = 0x%x, permissions = %s)' % (addr
, packet
.str))
1052 def rsp_memory_bytes(options
, cmd
, cmd_args
, rsp
):
1053 addr
= Packet(cmd_args
).get_hex_uint('big')
1054 dump_hex_memory_buffer(addr
, rsp
)
1057 def get_register_name_equal_value(options
, reg_num
, hex_value_str
):
1058 if reg_num
< len(g_register_infos
):
1059 reg_info
= g_register_infos
[reg_num
]
1060 value_str
= reg_info
.get_value_from_hex_string(hex_value_str
)
1061 s
= reg_info
.name() + ' = '
1062 if options
.symbolicator
:
1063 symbolicated_addresses
= options
.symbolicator
.symbolicate(
1065 if symbolicated_addresses
:
1066 s
+= options
.colors
.magenta()
1067 s
+= '%s' % symbolicated_addresses
[0]
1068 s
+= options
.colors
.reset()
1073 reg_value
= Packet(hex_value_str
).get_hex_uint(g_byte_order
)
1074 return 'reg(%u) = 0x%x' % (reg_num
, reg_value
)
1077 def cmd_read_one_reg(options
, cmd
, args
):
1078 packet
= Packet(args
)
1079 reg_num
= packet
.get_hex_uint('big')
1080 tid
= get_thread_from_thread_suffix(packet
.str)
1082 if reg_num
< len(g_register_infos
):
1083 name
= g_register_infos
[reg_num
].name()
1085 packet
.get_char() # skip ;
1086 thread_info
= packet
.get_key_value_pairs()
1087 tid
= int(thread_info
[0][1], 16)
1088 s
= 'read_register (reg_num=%u' % reg_num
1090 s
+= ' (%s)' % (name
)
1092 s
+= ', tid = 0x%4.4x' % (tid
)
1098 def rsp_read_one_reg(options
, cmd
, cmd_args
, rsp
):
1099 packet
= Packet(cmd_args
)
1100 reg_num
= packet
.get_hex_uint('big')
1101 print(get_register_name_equal_value(options
, reg_num
, rsp
))
1104 def cmd_write_one_reg(options
, cmd
, args
):
1105 packet
= Packet(args
)
1106 reg_num
= packet
.get_hex_uint('big')
1107 if packet
.get_char() != '=':
1108 print('error: invalid register write packet')
1111 hex_value_str
= packet
.get_hex_chars()
1112 tid
= get_thread_from_thread_suffix(packet
.str)
1113 s
= 'write_register (reg_num=%u' % reg_num
1115 s
+= ' (%s)' % (name
)
1117 s
+= get_register_name_equal_value(options
, reg_num
, hex_value_str
)
1119 s
+= ', tid = 0x%4.4x' % (tid
)
1125 def dump_all_regs(packet
):
1126 for reg_info
in g_register_infos
:
1127 nibble_size
= reg_info
.bit_size() / 4
1128 hex_value_str
= packet
.get_hex_chars(nibble_size
)
1129 if hex_value_str
is not None:
1130 value
= reg_info
.get_value_from_hex_string(hex_value_str
)
1131 print('%*s = %s' % (g_max_register_info_name_len
, reg_info
.name(), value
))
1136 def cmd_read_all_regs(cmd
, cmd_args
):
1137 packet
= Packet(cmd_args
)
1138 packet
.get_char() # toss the 'g' command character
1139 tid
= get_thread_from_thread_suffix(packet
.str)
1141 print('read_all_register(thread = 0x%4.4x)' % tid
)
1143 print('read_all_register()')
1147 def rsp_read_all_regs(options
, cmd
, cmd_args
, rsp
):
1148 packet
= Packet(rsp
)
1149 dump_all_regs(packet
)
1152 def cmd_write_all_regs(options
, cmd
, args
):
1153 packet
= Packet(args
)
1154 print('write_all_registers()')
1155 dump_all_regs(packet
)
1158 g_bp_types
= ["software_bp", "hardware_bp", "write_wp", "read_wp", "access_wp"]
1161 def cmd_bp(options
, cmd
, args
):
1166 packet
= Packet(args
)
1167 bp_type
= packet
.get_hex_uint('big')
1168 packet
.get_char() # Skip ,
1169 bp_addr
= packet
.get_hex_uint('big')
1170 packet
.get_char() # Skip ,
1171 bp_size
= packet
.get_hex_uint('big')
1172 s
+= g_bp_types
[bp_type
]
1173 s
+= " (addr = 0x%x, size = %u)" % (bp_addr
, bp_size
)
1178 def cmd_mem_rgn_info(options
, cmd
, args
):
1179 packet
= Packet(args
)
1180 packet
.get_char() # skip ':' character
1181 addr
= packet
.get_hex_uint('big')
1182 print('get_memory_region_info (addr=0x%x)' % (addr
))
1186 def cmd_kill(options
, cmd
, args
):
1187 print('kill_process()')
1191 def cmd_jThreadsInfo(options
, cmd
, args
):
1192 print('jThreadsInfo()')
1196 def cmd_jGetLoadedDynamicLibrariesInfos(options
, cmd
, args
):
1197 print('jGetLoadedDynamicLibrariesInfos()')
1201 def decode_packet(s
, start_index
=0):
1202 # print '\ndecode_packet("%s")' % (s[start_index:])
1203 index
= s
.find('}', start_index
)
1204 have_escapes
= index
!= -1
1206 normal_s
= s
[start_index
:index
]
1208 normal_s
= s
[start_index
:]
1209 # print 'normal_s = "%s"' % (normal_s)
1211 escape_char
= '%c' % (ord(s
[index
+ 1]) ^
0x20)
1212 # print 'escape_char for "%s" = %c' % (s[index:index+2], escape_char)
1213 return normal_s
+ escape_char
+ decode_packet(s
, index
+ 2)
1218 def rsp_json(options
, cmd
, cmd_args
, rsp
):
1219 print('%s() reply:' % (cmd
))
1223 json_tree
= json
.loads(rsp
)
1224 print(json
.dumps(json_tree
, indent
=4, separators
=(',', ': ')))
1225 except json
.JSONDecodeError
:
1228 def rsp_jGetLoadedDynamicLibrariesInfos(options
, cmd
, cmd_args
, rsp
):
1230 rsp_json(options
, cmd
, cmd_args
, rsp
)
1232 rsp_ok_means_supported(options
, cmd
, cmd_args
, rsp
)
1234 gdb_remote_commands
= {
1235 '\\?': {'cmd': cmd_stop_reply
, 'rsp': rsp_stop_reply
, 'name': "stop reply pacpket"},
1236 'qThreadStopInfo': {'cmd': cmd_qThreadStopInfo
, 'rsp': rsp_stop_reply
, 'name': "stop reply pacpket"},
1237 'QStartNoAckMode': {'cmd': cmd_query_packet
, 'rsp': rsp_ok_means_supported
, 'name': "query if no ack mode is supported"},
1238 'QThreadSuffixSupported': {'cmd': cmd_query_packet
, 'rsp': rsp_ok_means_supported
, 'name': "query if thread suffix is supported"},
1239 'QListThreadsInStopReply': {'cmd': cmd_query_packet
, 'rsp': rsp_ok_means_supported
, 'name': "query if threads in stop reply packets are supported"},
1240 'QSetDetachOnError:': {'cmd': cmd_QSetWithUnsigned
, 'rsp': rsp_ok_means_success
, 'name': "set if we should detach on error"},
1241 'QSetDisableASLR:': {'cmd': cmd_QSetWithUnsigned
, 'rsp': rsp_ok_means_success
, 'name': "set if we should disable ASLR"},
1242 'qLaunchSuccess': {'cmd': cmd_query_packet
, 'rsp': rsp_ok_means_success
, 'name': "check on launch success for the A packet"},
1243 'A': {'cmd': cmd_A
, 'rsp': rsp_ok_means_success
, 'name': "launch process"},
1244 'QLaunchArch:': {'cmd': cmd_QSetWithString
, 'rsp': rsp_ok_means_supported
, 'name': "set the arch to launch in case the file contains multiple architectures"},
1245 'qVAttachOrWaitSupported': {'cmd': cmd_query_packet
, 'rsp': rsp_ok_means_supported
, 'name': "set the launch architecture"},
1246 'qHostInfo': {'cmd': cmd_query_packet
, 'rsp': rsp_dump_key_value_pairs
, 'name': "get host information"},
1247 'qC': {'cmd': cmd_qC
, 'rsp': rsp_qC
, 'name': "return the current thread ID"},
1248 'vCont': {'cmd': cmd_vCont
, 'rsp': rsp_vCont
, 'name': "extended continue command"},
1249 'qSpeedTest': {'cmd':cmd_qSpeedTest
, 'rsp': rsp_qSpeedTest
, 'name': 'speed test packdet'},
1250 'vAttach': {'cmd': cmd_vAttach
, 'rsp': rsp_stop_reply
, 'name': "attach to process"},
1251 'c': {'cmd': cmd_c
, 'rsp': rsp_stop_reply
, 'name': "continue"},
1252 's': {'cmd': cmd_s
, 'rsp': rsp_stop_reply
, 'name': "step"},
1253 'qRegisterInfo': {'cmd': cmd_qRegisterInfo
, 'rsp': rsp_qRegisterInfo
, 'name': "query register info"},
1254 'qfThreadInfo': {'cmd': cmd_qThreadInfo
, 'rsp': rsp_qThreadInfo
, 'name': "get current thread list"},
1255 'qsThreadInfo': {'cmd': cmd_qThreadInfo
, 'rsp': rsp_qThreadInfo
, 'name': "get current thread list"},
1256 'qShlibInfoAddr': {'cmd': cmd_query_packet
, 'rsp': rsp_hex_big_endian
, 'name': "get shared library info address"},
1257 'qMemoryRegionInfo': {'cmd': cmd_mem_rgn_info
, 'rsp': rsp_dump_key_value_pairs
, 'name': "get memory region information"},
1258 'qProcessInfo': {'cmd': cmd_query_packet
, 'rsp': rsp_dump_key_value_pairs
, 'name': "get process info"},
1259 'qSupported': {'cmd': cmd_query_packet
, 'rsp': rsp_ok_means_supported
, 'name': "query supported"},
1260 'qXfer:': {'cmd': cmd_qXfer
, 'rsp': rsp_qXfer
, 'name': "qXfer"},
1261 'qSymbol:': {'cmd': cmd_qSymbol
, 'rsp': rsp_qSymbol
, 'name': "qSymbol"},
1262 'QSetSTDIN:' : {'cmd' : cmd_QSetWithHexString
, 'rsp' : rsp_ok_means_success
, 'name': "set STDIN prior to launching with A packet"},
1263 'QSetSTDOUT:' : {'cmd' : cmd_QSetWithHexString
, 'rsp' : rsp_ok_means_success
, 'name': "set STDOUT prior to launching with A packet"},
1264 'QSetSTDERR:' : {'cmd' : cmd_QSetWithHexString
, 'rsp' : rsp_ok_means_success
, 'name': "set STDERR prior to launching with A packet"},
1265 'QEnvironment:' : {'cmd' : cmd_QSetWithString
, 'rsp' : rsp_ok_means_success
, 'name': "set an environment variable prior to launching with A packet"},
1266 'QEnvironmentHexEncoded:' : {'cmd' : cmd_QSetWithHexString
, 'rsp' : rsp_ok_means_success
, 'name': "set an environment variable prior to launching with A packet"},
1267 'x': {'cmd': cmd_read_mem_bin
, 'rsp': rsp_mem_bin_bytes
, 'name': "read memory binary"},
1268 'X': {'cmd': cmd_write_memory
, 'rsp': rsp_ok_means_success
, 'name': "write memory binary"},
1269 'm': {'cmd': cmd_read_memory
, 'rsp': rsp_memory_bytes
, 'name': "read memory"},
1270 'M': {'cmd': cmd_write_memory
, 'rsp': rsp_ok_means_success
, 'name': "write memory"},
1271 '_M': {'cmd': cmd_alloc_memory
, 'rsp': rsp_alloc_memory
, 'name': "allocate memory"},
1272 '_m': {'cmd': cmd_dealloc_memory
, 'rsp': rsp_ok_means_success
, 'name': "deallocate memory"},
1273 'p': {'cmd': cmd_read_one_reg
, 'rsp': rsp_read_one_reg
, 'name': "read single register"},
1274 'P': {'cmd': cmd_write_one_reg
, 'rsp': rsp_ok_means_success
, 'name': "write single register"},
1275 'g': {'cmd': cmd_read_all_regs
, 'rsp': rsp_read_all_regs
, 'name': "read all registers"},
1276 'G': {'cmd': cmd_write_all_regs
, 'rsp': rsp_ok_means_success
, 'name': "write all registers"},
1277 'z': {'cmd': cmd_bp
, 'rsp': rsp_ok_means_success
, 'name': "clear breakpoint or watchpoint"},
1278 'Z': {'cmd': cmd_bp
, 'rsp': rsp_ok_means_success
, 'name': "set breakpoint or watchpoint"},
1279 'k': {'cmd': cmd_kill
, 'rsp': rsp_stop_reply
, 'name': "kill process"},
1280 'jThreadsInfo': {'cmd': cmd_jThreadsInfo
, 'rsp': rsp_json
, 'name': "JSON get all threads info"},
1281 'jGetLoadedDynamicLibrariesInfos:': {'cmd': cmd_jGetLoadedDynamicLibrariesInfos
, 'rsp': rsp_jGetLoadedDynamicLibrariesInfos
, 'name': 'JSON get loaded dynamic libraries'},
1285 def calculate_mean_and_standard_deviation(floats
):
1296 accum
+= delta
* delta
1298 std_dev
= math
.sqrt(accum
/ (count
- 1))
1299 return (mean
, std_dev
)
1302 def parse_gdb_log_file(path
, options
):
1304 parse_gdb_log(f
, options
)
1308 def round_up(n
, incr
):
1309 return float(((int(n
) + incr
) / incr
) * incr
)
1312 def plot_latencies(sec_times
):
1313 # import numpy as np
1314 import matplotlib
.pyplot
as plt
1316 for (i
, name
) in enumerate(sec_times
.keys()):
1317 times
= sec_times
[name
]
1320 plt
.subplot(2, 1, 1)
1321 plt
.title('Packet "%s" Times' % (name
))
1322 plt
.xlabel('Packet')
1327 time
= time
* 1000.0
1328 adj_times
.append(time
)
1334 for i
in range(len(adj_times
)):
1335 adj_times
[i
] *= 1000.0
1336 if adj_times
[i
] > max_time
:
1337 max_time
= adj_times
[i
]
1338 plt
.ylabel('Time (%s)' % (units
))
1340 for i
in [5.0, 10.0, 25.0, 50.0]:
1342 max_y
= round_up(max_time
, i
)
1345 max_y
= round_up(max_time
, 100.0)
1346 plt
.ylim(0.0, max_y
)
1347 plt
.plot(adj_times
, 'o-')
1351 def parse_gdb_log(file, options
):
1352 '''Parse a GDB log file that was generated by enabling logging with:
1353 (lldb) log enable --threadsafe --timestamp --file <FILE> gdb-remote packets
1354 This log file will contain timestamps and this function will then normalize
1355 those packets to be relative to the first value timestamp that is found and
1356 show delta times between log lines and also keep track of how long it takes
1357 for GDB remote commands to make a send/receive round trip. This can be
1358 handy when trying to figure out why some operation in the debugger is taking
1359 a long time during a preset set of debugger commands.'''
1361 tricky_commands
= ['qRegisterInfo']
1362 timestamp_regex
= re
.compile('(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$')
1363 packet_name_regex
= re
.compile('([A-Za-z_]+)[^a-z]')
1364 packet_transmit_name_regex
= re
.compile(
1365 '(?P<direction>send|read) packet: (?P<packet>.*)')
1366 packet_contents_name_regex
= re
.compile('\$([^#]*)#[0-9a-fA-F]{2}')
1367 packet_checksum_regex
= re
.compile('.*#[0-9a-fA-F]{2}$')
1368 packet_names_regex_str
= '(' + \
1369 '|'.join(gdb_remote_commands
.keys()) + ')(.*)'
1370 packet_names_regex
= re
.compile(packet_names_regex_str
)
1374 min_time
= 100000000.0
1375 packet_total_times
= {}
1376 all_packet_times
= []
1379 lines
= file.read().splitlines()
1381 last_command_args
= None
1382 last_command_packet
= None
1383 hide_next_response
= False
1384 num_lines
= len(lines
)
1386 for (line_index
, line
) in enumerate(lines
):
1387 # See if we need to skip any lines
1391 m
= packet_transmit_name_regex
.search(line
)
1395 direction
= m
.group('direction')
1396 is_command
= direction
== 'send'
1397 packet
= m
.group('packet')
1398 sys
.stdout
.write(options
.colors
.green())
1399 if not options
.quiet
and not hide_next_response
:
1401 sys
.stdout
.write(options
.colors
.reset())
1403 # print 'direction = "%s", packet = "%s"' % (direction, packet)
1405 if packet
[0] == '+':
1407 print('-->', end
=' ')
1409 print('<--', end
=' ')
1410 if not options
.quiet
:
1413 elif packet
[0] == '-':
1415 print('-->', end
=' ')
1417 print('<--', end
=' ')
1418 if not options
.quiet
:
1421 elif packet
[0] == '$':
1422 m
= packet_contents_name_regex
.match(packet
)
1423 if not m
and packet
[0] == '$':
1424 multiline_packet
= packet
1425 idx
= line_index
+ 1
1426 while idx
< num_lines
:
1427 if not options
.quiet
and not hide_next_response
:
1428 print('# ', lines
[idx
])
1429 multiline_packet
+= lines
[idx
]
1430 m
= packet_contents_name_regex
.match(multiline_packet
)
1432 packet
= multiline_packet
1433 skip_count
= idx
- line_index
1439 print('-->', end
=' ')
1441 print('<--', end
=' ')
1442 contents
= decode_packet(m
.group(1))
1444 hide_next_response
= False
1445 m
= packet_names_regex
.match(contents
)
1447 last_command
= m
.group(1)
1448 if last_command
== '?':
1449 last_command
= '\\?'
1450 packet_name
= last_command
1451 last_command_args
= m
.group(2)
1452 last_command_packet
= contents
1453 hide_next_response
= gdb_remote_commands
[last_command
][
1454 'cmd'](options
, last_command
, last_command_args
)
1456 packet_match
= packet_name_regex
.match(contents
)
1458 packet_name
= packet_match
.group(1)
1459 for tricky_cmd
in tricky_commands
:
1460 if packet_name
.find(tricky_cmd
) == 0:
1461 packet_name
= tricky_cmd
1463 packet_name
= contents
1465 last_command_args
= None
1466 last_command_packet
= None
1468 gdb_remote_commands
[last_command
]['rsp'](
1469 options
, last_command
, last_command_args
, contents
)
1471 print('error: invalid packet: "', packet
, '"')
1476 match
= timestamp_regex
.match(line
)
1478 curr_time
= float(match
.group(2))
1479 if last_time
and not is_command
:
1480 delta
= curr_time
- last_time
1481 all_packet_times
.append(delta
)
1484 delta
= curr_time
- last_time
1486 base_time
= curr_time
1489 if line
.find('read packet: $') >= 0 and packet_name
:
1490 if packet_name
in packet_total_times
:
1491 packet_total_times
[packet_name
] += delta
1492 packet_counts
[packet_name
] += 1
1494 packet_total_times
[packet_name
] = delta
1495 packet_counts
[packet_name
] = 1
1496 if packet_name
not in packet_times
:
1497 packet_times
[packet_name
] = []
1498 packet_times
[packet_name
].append(delta
)
1500 if min_time
> delta
:
1503 if not options
or not options
.quiet
:
1504 print('%s%.6f %+.6f%s' % (match
.group(1),
1505 curr_time
- base_time
,
1508 last_time
= curr_time
1511 (average
, std_dev
) = calculate_mean_and_standard_deviation(all_packet_times
)
1512 if average
and std_dev
:
1513 print('%u packets with average packet time of %f and standard deviation of %f' % (len(all_packet_times
), average
, std_dev
))
1514 if packet_total_times
:
1515 total_packet_time
= 0.0
1516 total_packet_count
= 0
1517 for key
, vvv
in packet_total_times
.items():
1518 # print ' key = (%s) "%s"' % (type(key), key)
1519 # print 'value = (%s) %s' % (type(vvv), vvv)
1520 # if type(vvv) == 'float':
1521 total_packet_time
+= vvv
1522 for key
, vvv
in packet_counts
.items():
1523 total_packet_count
+= vvv
1525 print('#------------------------------------------------------------')
1526 print('# Packet timing summary:')
1527 print('# Totals: time = %6f, count = %6d' % (total_packet_time
,
1528 total_packet_count
))
1529 print('# Min packet time: time = %6f' % (min_time
))
1530 print('#------------------------------------------------------------')
1531 print('# Packet Time (sec) Percent Count Latency')
1532 print('#------------------------- ----------- ------- ------ -------')
1533 if options
and options
.sort_count
:
1536 key
=packet_counts
.__getitem
__,
1541 key
=packet_total_times
.__getitem
__,
1546 packet_total_time
= packet_total_times
[item
]
1548 packet_total_time
/ total_packet_time
) * 100.0
1549 packet_count
= packet_counts
[item
]
1550 print(" %24s %11.6f %5.2f%% %6d %9.6f" % (
1551 item
, packet_total_time
, packet_percent
, packet_count
,
1552 float(packet_total_time
) / float(packet_count
)))
1553 if options
and options
.plot
:
1554 plot_latencies(packet_times
)
1556 if __name__
== '__main__':
1557 usage
= "usage: gdbremote [options]"
1558 description
= '''The command disassembles a GDB remote packet log.'''
1559 parser
= optparse
.OptionParser(
1560 description
=description
,
1566 action
='store_true',
1568 help='display verbose debug info',
1573 action
='store_true',
1575 help='display verbose debug info',
1580 action
='store_true',
1582 help='add terminal colors',
1587 action
='store_true',
1589 help='display verbose debug info',
1595 help='symbolicate using a darwin crash log file',
1598 (options
, args
) = parser
.parse_args(sys
.argv
[1:])
1600 print('error: argument error')
1603 options
.colors
= TerminalColors(options
.color
)
1604 options
.symbolicator
= None
1605 if options
.crashlog
:
1607 lldb
.debugger
= lldb
.SBDebugger
.Create()
1608 import lldb
.macosx
.crashlog
1609 options
.symbolicator
= lldb
.macosx
.crashlog
.CrashLog(options
.crashlog
)
1610 print('%s' % (options
.symbolicator
))
1612 # This script is being run from the command line, create a debugger in case we are
1613 # going to use any debugger functions in our function.
1616 print('#----------------------------------------------------------------------')
1617 print("# GDB remote log file: '%s'" % file)
1618 print('#----------------------------------------------------------------------')
1619 parse_gdb_log_file(file, options
)
1620 if options
.symbolicator
:
1621 print('%s' % (options
.symbolicator
))
1623 parse_gdb_log(sys
.stdin
, options
)
1625 def __lldb_init_module(debugger
, internal_dict
):
1626 # This initializer is being run from LLDB in the embedded command interpreter
1627 # Add any commands contained in this module to LLDB
1628 debugger
.HandleCommand(
1629 'command script add -o -f gdbremote.start_gdb_log start_gdb_log')
1630 debugger
.HandleCommand(
1631 'command script add -o -f gdbremote.stop_gdb_log stop_gdb_log')
1632 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')