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.
111 def red(self
, fg
=True):
112 """Set the foreground or background color to red.
113 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.
122 def green(self
, fg
=True):
123 """Set the foreground or background color to green.
124 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.
133 def yellow(self
, fg
=True):
134 """Set the foreground or background color to yellow.
135 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.
144 def blue(self
, fg
=True):
145 """Set the foreground or background color to blue.
146 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.
155 def magenta(self
, fg
=True):
156 """Set the foreground or background color to magenta.
157 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.
166 def cyan(self
, fg
=True):
167 """Set the foreground or background color to cyan.
168 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.
177 def white(self
, fg
=True):
178 """Set the foreground or background color to white.
179 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.
188 def default(self
, fg
=True):
189 """Set the foreground or background color to the default.
190 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.
200 def start_gdb_log(debugger
, command
, result
, dict):
201 """Start logging GDB remote packets by enabling logging with timestamps and
202 thread safe logging. Follow a call to this function with a call to "stop_gdb_log"
203 in order to dump out the commands."""
205 command_args
= shlex
.split(command
)
206 usage
= "usage: start_gdb_log [options] [<LOGFILEPATH>]"
207 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
208 be aggregated and displayed."""
209 parser
= optparse
.OptionParser(
210 description
=description
, prog
="start_gdb_log", usage
=usage
217 help="display verbose debug info",
221 (options
, args
) = parser
.parse_args(command_args
)
227 'error: logging is already in progress with file "%s"' % g_log_file
232 g_log_file
= tempfile
.mktemp()
237 debugger
.HandleCommand(
238 'log enable --threadsafe --timestamp --file "%s" gdb-remote packets'
242 "GDB packet logging enable with log file '%s'\nUse the 'stop_gdb_log' command to stop logging and show packet statistics."
247 result
.PutCString("error: invalid log file path")
248 result
.PutCString(usage
)
251 def stop_gdb_log(debugger
, command
, result
, dict):
252 """Stop logging GDB remote packets to the file that was specified in a call
253 to "start_gdb_log" and normalize the timestamps to be relative to the first
254 timestamp in the log file. Also print out statistics for how long each
255 command took to allow performance bottlenecks to be determined."""
257 # Any commands whose names might be followed by more valid C identifier
258 # characters must be listed here
259 command_args
= shlex
.split(command
)
260 usage
= "usage: stop_gdb_log [options]"
261 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."""
262 parser
= optparse
.OptionParser(
263 description
=description
, prog
="stop_gdb_log", usage
=usage
270 help="display verbose debug info",
277 help="plot packet latencies by packet type",
285 help="display verbose debug info",
293 help="add terminal colors",
301 help="display verbose debug info",
309 help='symbolicate addresses in log using current "lldb.target"',
313 (options
, args
) = parser
.parse_args(command_args
)
316 options
.colors
= TerminalColors(options
.color
)
317 options
.symbolicator
= None
318 if options
.symbolicate
:
320 import lldb
.utils
.symbolication
322 options
.symbolicator
= lldb
.utils
.symbolication
.Symbolicator()
323 options
.symbolicator
.target
= lldb
.target
325 print("error: can't symbolicate without a target")
329 'error: logging must have been previously enabled with a call to "stop_gdb_log"'
331 elif os
.path
.exists(g_log_file
):
333 debugger
.HandleCommand("log disable gdb-remote packets")
335 "GDB packet logging disabled. Logged packets are in '%s'" % g_log_file
337 parse_gdb_log_file(g_log_file
, options
)
339 result
.PutCString(usage
)
341 print('error: the GDB packet log file "%s" does not exist' % g_log_file
)
344 def is_hex_byte(str):
346 return str[0] in string
.hexdigits
and str[1] in string
.hexdigits
350 def get_hex_string_if_all_printable(str):
352 s
= binascii
.unhexlify(str).decode()
353 if all(c
in string
.printable
for c
in s
):
355 except (TypeError, binascii
.Error
, UnicodeDecodeError):
360 # global register info list
361 g_register_infos
= list()
362 g_max_register_info_name_len
= 0
366 """Class that represents register information"""
368 def __init__(self
, kvp
):
373 self
.info
[key
] = value
376 """Get the name of the register."""
377 if self
.info
and "name" in self
.info
:
378 return self
.info
["name"]
382 """Get the size in bits of the register."""
383 if self
.info
and "bitsize" in self
.info
:
384 return int(self
.info
["bitsize"])
388 """Get the size in bytes of the register."""
389 return self
.bit_size() / 8
391 def get_value_from_hex_string(self
, hex_str
):
392 """Dump the register value given a native byte order encoded hex ASCII byte string."""
393 encoding
= self
.info
["encoding"]
394 bit_size
= self
.bit_size()
395 packet
= Packet(hex_str
)
396 if encoding
== "uint":
397 uval
= packet
.get_hex_uint(g_byte_order
)
399 return "0x%2.2x" % (uval
)
401 return "0x%4.4x" % (uval
)
403 return "0x%8.8x" % (uval
)
405 return "0x%16.16x" % (uval
)
407 uval
= packet
.get_hex_uint8()
408 while uval
is not None:
410 uval
= packet
.get_hex_uint8()
412 if g_byte_order
== "little":
415 value_str
+= "%2.2x" % byte
416 return "%s" % (value_str
)
419 """Dump the register info key/value pairs"""
421 for key
in self
.info
.keys():
424 s
+= "%s=%s " % (key
, self
.info
[key
])
429 """Class that represents a packet that contains string data"""
431 def __init__(self
, packet_str
):
432 self
.str = packet_str
444 self
.str = self
.str[1:]
447 def skip_exact_string(self
, s
):
448 if self
.str and self
.str.startswith(s
):
449 self
.str = self
.str[len(s
) :]
454 def get_thread_id(self
, fail_value
=-1):
455 match
= g_number_regex
.match(self
.str)
457 number_str
= match
.group(1)
458 self
.str = self
.str[len(number_str
) :]
459 return int(number_str
, 0)
463 def get_hex_uint8(self
):
466 and len(self
.str) >= 2
467 and self
.str[0] in string
.hexdigits
468 and self
.str[1] in string
.hexdigits
470 uval
= int(self
.str[0:2], 16)
471 self
.str = self
.str[2:]
475 def get_hex_uint16(self
, byte_order
):
477 if byte_order
== "big":
478 uval |
= self
.get_hex_uint8() << 8
479 uval |
= self
.get_hex_uint8()
481 uval |
= self
.get_hex_uint8()
482 uval |
= self
.get_hex_uint8() << 8
485 def get_hex_uint32(self
, byte_order
):
487 if byte_order
== "big":
488 uval |
= self
.get_hex_uint8() << 24
489 uval |
= self
.get_hex_uint8() << 16
490 uval |
= self
.get_hex_uint8() << 8
491 uval |
= self
.get_hex_uint8()
493 uval |
= self
.get_hex_uint8()
494 uval |
= self
.get_hex_uint8() << 8
495 uval |
= self
.get_hex_uint8() << 16
496 uval |
= self
.get_hex_uint8() << 24
499 def get_hex_uint64(self
, byte_order
):
501 if byte_order
== "big":
502 uval |
= self
.get_hex_uint8() << 56
503 uval |
= self
.get_hex_uint8() << 48
504 uval |
= self
.get_hex_uint8() << 40
505 uval |
= self
.get_hex_uint8() << 32
506 uval |
= self
.get_hex_uint8() << 24
507 uval |
= self
.get_hex_uint8() << 16
508 uval |
= self
.get_hex_uint8() << 8
509 uval |
= self
.get_hex_uint8()
511 uval |
= self
.get_hex_uint8()
512 uval |
= self
.get_hex_uint8() << 8
513 uval |
= self
.get_hex_uint8() << 16
514 uval |
= self
.get_hex_uint8() << 24
515 uval |
= self
.get_hex_uint8() << 32
516 uval |
= self
.get_hex_uint8() << 40
517 uval |
= self
.get_hex_uint8() << 48
518 uval |
= self
.get_hex_uint8() << 56
521 def get_number(self
, fail_value
=-1):
522 """Get a number from the packet. The number must be in big endian format and should be parsed
523 according to its prefix (starts with "0x" means hex, starts with "0" means octal, starts with
524 [1-9] means decimal, etc)"""
525 match
= g_number_regex
.match(self
.str)
527 number_str
= match
.group(1)
528 self
.str = self
.str[len(number_str
) :]
529 return int(number_str
, 0)
533 def get_hex_ascii_str(self
, n
=0):
534 hex_chars
= self
.get_hex_chars(n
)
536 return binascii
.unhexlify(hex_chars
)
540 def get_hex_chars(self
, n
=0):
541 str_len
= len(self
.str)
543 # n was zero, so we need to determine all hex chars and
544 # stop when we hit the end of the string of a non-hex character
545 while n
< str_len
and self
.str[n
] in string
.hexdigits
:
549 return None # Not enough chars
550 # Verify all chars are hex if a length was specified
552 if self
.str[i
] not in string
.hexdigits
:
553 return None # Not all hex digits
556 hex_str
= self
.str[0:n
]
557 self
.str = self
.str[n
:]
560 def get_hex_uint(self
, byte_order
, n
=0):
561 if byte_order
== "big":
562 hex_str
= self
.get_hex_chars(n
)
565 return int(hex_str
, 16)
567 uval
= self
.get_hex_uint8()
572 while uval
is not None:
573 uval_result |
= uval
<< shift
575 uval
= self
.get_hex_uint8()
578 def get_key_value_pairs(self
):
581 key_value_pairs
= self
.str.split(";")
582 for key_value_pair
in key_value_pairs
:
583 if len(key_value_pair
):
584 kvp
.append(key_value_pair
.split(":", 1))
588 return self
.str.split(ch
)
590 def split_hex(self
, ch
, byte_order
):
592 strings
= self
.str.split(ch
)
594 hex_values
.append(Packet(str).get_hex_uint(byte_order
))
604 g_thread_suffix_regex
= re
.compile(";thread:([0-9a-fA-F]+);")
607 def get_thread_from_thread_suffix(str):
609 match
= g_thread_suffix_regex
.match(str)
611 return int(match
.group(1), 16)
615 def cmd_qThreadStopInfo(options
, cmd
, args
):
616 packet
= Packet(args
)
617 tid
= packet
.get_hex_uint("big")
618 print("get_thread_stop_info (tid = 0x%x)" % (tid
))
621 def cmd_stop_reply(options
, cmd
, args
):
622 print("get_last_stop_info()")
626 def rsp_stop_reply(options
, cmd
, cmd_args
, rsp
):
629 stop_type
= packet
.get_char()
630 if stop_type
== "T" or stop_type
== "S":
631 signo
= packet
.get_hex_uint8()
632 key_value_pairs
= packet
.get_key_value_pairs()
633 for key_value_pair
in key_value_pairs
:
634 key
= key_value_pair
[0]
636 reg_num
= Packet(key
).get_hex_uint8()
637 if reg_num
< len(g_register_infos
):
638 reg_info
= g_register_infos
[reg_num
]
639 key_value_pair
[0] = reg_info
.name()
640 key_value_pair
[1] = reg_info
.get_value_from_hex_string(
643 elif key
== "jthreads" or key
== "jstopinfo":
644 key_value_pair
[1] = binascii
.unhexlify(key_value_pair
[1])
645 key_value_pairs
.insert(0, ["signal", signo
])
646 print("stop_reply():")
647 dump_key_value_pairs(key_value_pairs
)
648 elif stop_type
== "W":
649 exit_status
= packet
.get_hex_uint8()
650 print("stop_reply(): exit (status=%i)" % exit_status
)
651 elif stop_type
== "O":
652 print('stop_reply(): stdout = "%s"' % packet
.str)
655 def cmd_unknown_packet(options
, cmd
, args
):
657 print("cmd: %s, args: %s", cmd
, args
)
659 print("cmd: %s", cmd
)
663 def cmd_qSymbol(options
, cmd
, args
):
665 print("ready to serve symbols")
667 packet
= Packet(args
)
668 symbol_addr
= packet
.get_hex_uint("big")
669 if symbol_addr
is None:
670 if packet
.skip_exact_string(":"):
671 symbol_name
= packet
.get_hex_ascii_str()
672 print('lookup_symbol("%s") -> symbol not available yet' % (symbol_name
))
674 print("error: bad command format")
676 if packet
.skip_exact_string(":"):
677 symbol_name
= packet
.get_hex_ascii_str()
678 print('lookup_symbol("%s") -> 0x%x' % (symbol_name
, symbol_addr
))
680 print("error: bad command format")
683 def cmd_QSetWithHexString(options
, cmd
, args
):
684 print('%s("%s")' % (cmd
[:-1], binascii
.unhexlify(args
)))
687 def cmd_QSetWithString(options
, cmd
, args
):
688 print('%s("%s")' % (cmd
[:-1], args
))
691 def cmd_QSetWithUnsigned(options
, cmd
, args
):
692 print("%s(%i)" % (cmd
[:-1], int(args
)))
695 def rsp_qSymbol(options
, cmd
, cmd_args
, rsp
):
700 print("No more symbols to lookup")
703 if packet
.skip_exact_string("qSymbol:"):
704 symbol_name
= packet
.get_hex_ascii_str()
705 print('lookup_symbol("%s")' % (symbol_name
))
708 'error: response string should start with "qSymbol:": respnse is "%s"'
713 def cmd_qXfer(options
, cmd
, args
):
714 # $qXfer:features:read:target.xml:0,1ffff#14
715 print("read target special data %s" % (args
))
719 def rsp_qXfer(options
, cmd
, cmd_args
, rsp
):
720 data
= cmd_args
.split(":")
721 if data
[0] == "features":
722 if data
[1] == "read":
723 filename
, extension
= os
.path
.splitext(data
[2])
724 if extension
== ".xml":
725 response
= Packet(rsp
)
726 xml_string
= response
.get_hex_ascii_str()
730 xml_string
= xml_string
[1:]
731 xml_root
= ET
.fromstring(xml_string
)
732 for reg_element
in xml_root
.findall("./feature/reg"):
733 if not "value_regnums" in reg_element
.attrib
:
734 reg_info
= RegisterInfo([])
735 if "name" in reg_element
.attrib
:
736 reg_info
.info
["name"] = reg_element
.attrib
["name"]
738 reg_info
.info
["name"] = "unspecified"
739 if "encoding" in reg_element
.attrib
:
740 reg_info
.info
["encoding"] = reg_element
.attrib
[
744 reg_info
.info
["encoding"] = "uint"
745 if "offset" in reg_element
.attrib
:
746 reg_info
.info
["offset"] = reg_element
.attrib
[
749 if "bitsize" in reg_element
.attrib
:
750 reg_info
.info
["bitsize"] = reg_element
.attrib
[
753 g_register_infos
.append(reg_info
)
754 print('XML for "%s":' % (data
[2]))
758 def cmd_A(options
, cmd
, args
):
759 print("launch process:")
760 packet
= Packet(args
)
762 arg_len
= packet
.get_number()
765 if not packet
.skip_exact_string(","):
767 arg_idx
= packet
.get_number()
770 if not packet
.skip_exact_string(","):
772 arg_value
= packet
.get_hex_ascii_str(arg_len
)
773 print('argv[%u] = "%s"' % (arg_idx
, arg_value
))
776 def cmd_qC(options
, cmd
, args
):
777 print("query_current_thread_id()")
780 def rsp_qC(options
, cmd
, cmd_args
, rsp
):
782 if packet
.skip_exact_string("QC"):
783 tid
= packet
.get_thread_id()
784 print("current_thread_id = %#x" % (tid
))
786 print("current_thread_id = old thread ID")
789 def cmd_query_packet(options
, cmd
, args
):
791 print("%s%s" % (cmd
, args
))
797 def rsp_ok_error(rsp
):
801 def rsp_ok_means_supported(options
, cmd
, cmd_args
, rsp
):
803 print("%s%s is supported" % (cmd
, cmd_args
))
805 print("%s%s is not supported" % (cmd
, cmd_args
))
807 print("%s%s -> %s" % (cmd
, cmd_args
, rsp
))
810 def rsp_ok_means_success(options
, cmd
, cmd_args
, rsp
):
814 print("%s%s is not supported" % (cmd
, cmd_args
))
816 print("%s%s -> %s" % (cmd
, cmd_args
, rsp
))
819 def dump_key_value_pairs(key_value_pairs
):
821 for key_value_pair
in key_value_pairs
:
822 key_len
= len(key_value_pair
[0])
823 if max_key_len
< key_len
:
824 max_key_len
= key_len
825 for key_value_pair
in key_value_pairs
:
826 key
= key_value_pair
[0]
827 value
= key_value_pair
[1]
828 unhex_value
= get_hex_string_if_all_printable(value
)
830 print("%*s = %s (%s)" % (max_key_len
, key
, value
, unhex_value
))
832 print("%*s = %s" % (max_key_len
, key
, value
))
835 def rsp_dump_key_value_pairs(options
, cmd
, cmd_args
, rsp
):
837 print("%s response:" % (cmd
))
839 key_value_pairs
= packet
.get_key_value_pairs()
840 dump_key_value_pairs(key_value_pairs
)
842 print("not supported")
845 def cmd_c(options
, cmd
, args
):
850 def cmd_s(options
, cmd
, args
):
855 def cmd_qSpeedTest(options
, cmd
, args
):
856 print(("qSpeedTest: cmd='%s', args='%s'" % (cmd
, args
)))
859 def rsp_qSpeedTest(options
, cmd
, cmd_args
, rsp
):
860 print(("qSpeedTest: rsp='%s' cmd='%s', args='%s'" % (rsp
, cmd
, args
)))
863 def cmd_vCont(options
, cmd
, args
):
865 print("%s: get supported extended continue modes" % (cmd
))
867 got_other_threads
= 0
869 for thread_action
in args
[1:].split(";"):
870 (short_action
, thread
) = thread_action
.split(":", 1)
871 tid
= int(thread
, 16)
872 if short_action
== "c":
874 elif short_action
== "s":
876 elif short_action
[0] == "C":
877 action
= "continue with signal 0x%s" % (short_action
[1:])
878 elif short_action
== "S":
879 action
= "step with signal 0x%s" % (short_action
[1:])
881 action
= short_action
885 got_other_threads
= 1
886 s
+= "other-threads:"
888 s
+= "thread 0x%4.4x: %s" % (tid
, action
)
889 if got_other_threads
:
890 print("extended_continue (%s)" % (s
))
892 print("extended_continue (%s, other-threads: suspend)" % (s
))
896 def rsp_vCont(options
, cmd
, cmd_args
, rsp
):
898 # Skip the leading 'vCont;'
900 modes
= rsp
.split(";")
901 s
= "%s: supported extended continue modes include: " % (cmd
)
903 for i
, mode
in enumerate(modes
):
909 s
+= "continue with signal"
913 s
+= "step with signal"
917 # s += 'unrecognized vCont mode: ', str(mode)
920 if rsp
[0] == "T" or rsp
[0] == "S" or rsp
[0] == "W" or rsp
[0] == "X":
921 rsp_stop_reply(options
, cmd
, cmd_args
, rsp
)
924 print("stdout: %s" % (rsp
))
928 "not supported (cmd = '%s', args = '%s', rsp = '%s')" % (cmd
, cmd_args
, rsp
)
932 def cmd_vAttach(options
, cmd
, args
):
933 (extra_command
, args
) = args
.split(";")
935 print("%s%s(%s)" % (cmd
, extra_command
, args
))
937 print("attach(pid = %u)" % int(args
, 16))
941 def cmd_qRegisterInfo(options
, cmd
, args
):
942 print("query_register_info(reg_num=%i)" % (int(args
, 16)))
946 def rsp_qRegisterInfo(options
, cmd
, cmd_args
, rsp
):
947 global g_max_register_info_name_len
948 print("query_register_info(reg_num=%i):" % (int(cmd_args
, 16)), end
=" ")
949 if len(rsp
) == 3 and rsp
[0] == "E":
950 g_max_register_info_name_len
= 0
951 for reg_info
in g_register_infos
:
952 name_len
= len(reg_info
.name())
953 if g_max_register_info_name_len
< name_len
:
954 g_max_register_info_name_len
= name_len
958 reg_info
= RegisterInfo(packet
.get_key_value_pairs())
959 g_register_infos
.append(reg_info
)
964 def cmd_qThreadInfo(options
, cmd
, args
):
965 if cmd
== "qfThreadInfo":
968 query_type
= "subsequent"
969 print("get_current_thread_list(type=%s)" % (query_type
))
973 def rsp_qThreadInfo(options
, cmd
, cmd_args
, rsp
):
975 response_type
= packet
.get_char()
976 if response_type
== "m":
977 tids
= packet
.split_hex(";", "big")
978 for i
, tid
in enumerate(tids
):
981 print("0x%x" % (tid
), end
=" ")
983 elif response_type
== "l":
987 def rsp_hex_big_endian(options
, cmd
, cmd_args
, rsp
):
989 print("%s%s is not supported" % (cmd
, cmd_args
))
992 uval
= packet
.get_hex_uint("big")
993 print("%s: 0x%x" % (cmd
, uval
))
996 def cmd_read_mem_bin(options
, cmd
, args
):
997 # x0x7fff5fc39200,0x200
998 packet
= Packet(args
)
999 addr
= packet
.get_hex_uint("big")
1000 comma
= packet
.get_char()
1001 size
= packet
.get_hex_uint("big")
1002 print("binary_read_memory (addr = 0x%16.16x, size = %u)" % (addr
, size
))
1006 def rsp_mem_bin_bytes(options
, cmd
, cmd_args
, rsp
):
1007 packet
= Packet(cmd_args
)
1008 addr
= packet
.get_hex_uint("big")
1009 comma
= packet
.get_char()
1010 size
= packet
.get_hex_uint("big")
1013 dump_hex_memory_buffer(addr
, rsp
)
1016 def cmd_read_memory(options
, cmd
, args
):
1017 packet
= Packet(args
)
1018 addr
= packet
.get_hex_uint("big")
1019 comma
= packet
.get_char()
1020 size
= packet
.get_hex_uint("big")
1021 print("read_memory (addr = 0x%16.16x, size = %u)" % (addr
, size
))
1025 def dump_hex_memory_buffer(addr
, hex_byte_str
):
1026 packet
= Packet(hex_byte_str
)
1029 uval
= packet
.get_hex_uint8()
1030 while uval
is not None:
1035 print("0x%x:" % (addr
+ idx
), end
=" ")
1036 print("%2.2x" % (uval
), end
=" ")
1037 if 0x20 <= uval
and uval
< 0x7F:
1038 ascii
+= "%c" % uval
1041 uval
= packet
.get_hex_uint8()
1048 def cmd_write_memory(options
, cmd
, args
):
1049 packet
= Packet(args
)
1050 addr
= packet
.get_hex_uint("big")
1051 if packet
.get_char() != ",":
1052 print("error: invalid write memory command (missing comma after address)")
1054 size
= packet
.get_hex_uint("big")
1055 if packet
.get_char() != ":":
1056 print("error: invalid write memory command (missing colon after size)")
1058 print("write_memory (addr = 0x%16.16x, size = %u, data:" % (addr
, size
))
1059 dump_hex_memory_buffer(addr
, packet
.str)
1063 def cmd_alloc_memory(options
, cmd
, args
):
1064 packet
= Packet(args
)
1065 byte_size
= packet
.get_hex_uint("big")
1066 if packet
.get_char() != ",":
1067 print("error: invalid allocate memory command (missing comma after address)")
1070 "allocate_memory (byte-size = %u (0x%x), permissions = %s)"
1071 % (byte_size
, byte_size
, packet
.str)
1076 def rsp_alloc_memory(options
, cmd
, cmd_args
, rsp
):
1077 packet
= Packet(rsp
)
1078 addr
= packet
.get_hex_uint("big")
1079 print("addr = 0x%x" % addr
)
1082 def cmd_dealloc_memory(options
, cmd
, args
):
1083 packet
= Packet(args
)
1084 addr
= packet
.get_hex_uint("big")
1085 if packet
.get_char() != ",":
1086 print("error: invalid allocate memory command (missing comma after address)")
1088 print("deallocate_memory (addr = 0x%x, permissions = %s)" % (addr
, packet
.str))
1092 def rsp_memory_bytes(options
, cmd
, cmd_args
, rsp
):
1093 addr
= Packet(cmd_args
).get_hex_uint("big")
1094 dump_hex_memory_buffer(addr
, rsp
)
1097 def get_register_name_equal_value(options
, reg_num
, hex_value_str
):
1098 if reg_num
< len(g_register_infos
):
1099 reg_info
= g_register_infos
[reg_num
]
1100 value_str
= reg_info
.get_value_from_hex_string(hex_value_str
)
1101 s
= reg_info
.name() + " = "
1102 if options
.symbolicator
:
1103 symbolicated_addresses
= options
.symbolicator
.symbolicate(int(value_str
, 0))
1104 if symbolicated_addresses
:
1105 s
+= options
.colors
.magenta()
1106 s
+= "%s" % symbolicated_addresses
[0]
1107 s
+= options
.colors
.reset()
1112 reg_value
= Packet(hex_value_str
).get_hex_uint(g_byte_order
)
1113 return "reg(%u) = 0x%x" % (reg_num
, reg_value
)
1116 def cmd_read_one_reg(options
, cmd
, args
):
1117 packet
= Packet(args
)
1118 reg_num
= packet
.get_hex_uint("big")
1119 tid
= get_thread_from_thread_suffix(packet
.str)
1121 if reg_num
< len(g_register_infos
):
1122 name
= g_register_infos
[reg_num
].name()
1124 packet
.get_char() # skip ;
1125 thread_info
= packet
.get_key_value_pairs()
1126 tid
= int(thread_info
[0][1], 16)
1127 s
= "read_register (reg_num=%u" % reg_num
1129 s
+= " (%s)" % (name
)
1131 s
+= ", tid = 0x%4.4x" % (tid
)
1137 def rsp_read_one_reg(options
, cmd
, cmd_args
, rsp
):
1138 packet
= Packet(cmd_args
)
1139 reg_num
= packet
.get_hex_uint("big")
1140 print(get_register_name_equal_value(options
, reg_num
, rsp
))
1143 def cmd_write_one_reg(options
, cmd
, args
):
1144 packet
= Packet(args
)
1145 reg_num
= packet
.get_hex_uint("big")
1146 if packet
.get_char() != "=":
1147 print("error: invalid register write packet")
1150 hex_value_str
= packet
.get_hex_chars()
1151 tid
= get_thread_from_thread_suffix(packet
.str)
1152 s
= "write_register (reg_num=%u" % reg_num
1154 s
+= " (%s)" % (name
)
1156 s
+= get_register_name_equal_value(options
, reg_num
, hex_value_str
)
1158 s
+= ", tid = 0x%4.4x" % (tid
)
1164 def dump_all_regs(packet
):
1165 for reg_info
in g_register_infos
:
1166 nibble_size
= reg_info
.bit_size() / 4
1167 hex_value_str
= packet
.get_hex_chars(nibble_size
)
1168 if hex_value_str
is not None:
1169 value
= reg_info
.get_value_from_hex_string(hex_value_str
)
1170 print("%*s = %s" % (g_max_register_info_name_len
, reg_info
.name(), value
))
1175 def cmd_read_all_regs(cmd
, cmd_args
):
1176 packet
= Packet(cmd_args
)
1177 packet
.get_char() # toss the 'g' command character
1178 tid
= get_thread_from_thread_suffix(packet
.str)
1180 print("read_all_register(thread = 0x%4.4x)" % tid
)
1182 print("read_all_register()")
1186 def rsp_read_all_regs(options
, cmd
, cmd_args
, rsp
):
1187 packet
= Packet(rsp
)
1188 dump_all_regs(packet
)
1191 def cmd_write_all_regs(options
, cmd
, args
):
1192 packet
= Packet(args
)
1193 print("write_all_registers()")
1194 dump_all_regs(packet
)
1198 g_bp_types
= ["software_bp", "hardware_bp", "write_wp", "read_wp", "access_wp"]
1201 def cmd_bp(options
, cmd
, args
):
1206 packet
= Packet(args
)
1207 bp_type
= packet
.get_hex_uint("big")
1208 packet
.get_char() # Skip ,
1209 bp_addr
= packet
.get_hex_uint("big")
1210 packet
.get_char() # Skip ,
1211 bp_size
= packet
.get_hex_uint("big")
1212 s
+= g_bp_types
[bp_type
]
1213 s
+= " (addr = 0x%x, size = %u)" % (bp_addr
, bp_size
)
1218 def cmd_mem_rgn_info(options
, cmd
, args
):
1219 packet
= Packet(args
)
1220 packet
.get_char() # skip ':' character
1221 addr
= packet
.get_hex_uint("big")
1222 print("get_memory_region_info (addr=0x%x)" % (addr
))
1226 def cmd_kill(options
, cmd
, args
):
1227 print("kill_process()")
1231 def cmd_jThreadsInfo(options
, cmd
, args
):
1232 print("jThreadsInfo()")
1236 def cmd_jGetLoadedDynamicLibrariesInfos(options
, cmd
, args
):
1237 print("jGetLoadedDynamicLibrariesInfos()")
1241 def decode_packet(s
, start_index
=0):
1242 # print '\ndecode_packet("%s")' % (s[start_index:])
1243 index
= s
.find("}", start_index
)
1244 have_escapes
= index
!= -1
1246 normal_s
= s
[start_index
:index
]
1248 normal_s
= s
[start_index
:]
1249 # print 'normal_s = "%s"' % (normal_s)
1251 escape_char
= "%c" % (ord(s
[index
+ 1]) ^
0x20)
1252 # print 'escape_char for "%s" = %c' % (s[index:index+2], escape_char)
1253 return normal_s
+ escape_char
+ decode_packet(s
, index
+ 2)
1258 def rsp_json(options
, cmd
, cmd_args
, rsp
):
1259 print("%s() reply:" % (cmd
))
1263 json_tree
= json
.loads(rsp
)
1264 print(json
.dumps(json_tree
, indent
=4, separators
=(",", ": ")))
1265 except json
.JSONDecodeError
:
1269 def rsp_jGetLoadedDynamicLibrariesInfos(options
, cmd
, cmd_args
, rsp
):
1271 rsp_json(options
, cmd
, cmd_args
, rsp
)
1273 rsp_ok_means_supported(options
, cmd
, cmd_args
, rsp
)
1276 gdb_remote_commands
= {
1277 "\\?": {"cmd": cmd_stop_reply
, "rsp": rsp_stop_reply
, "name": "stop reply pacpket"},
1278 "qThreadStopInfo": {
1279 "cmd": cmd_qThreadStopInfo
,
1280 "rsp": rsp_stop_reply
,
1281 "name": "stop reply pacpket",
1283 "QStartNoAckMode": {
1284 "cmd": cmd_query_packet
,
1285 "rsp": rsp_ok_means_supported
,
1286 "name": "query if no ack mode is supported",
1288 "QThreadSuffixSupported": {
1289 "cmd": cmd_query_packet
,
1290 "rsp": rsp_ok_means_supported
,
1291 "name": "query if thread suffix is supported",
1293 "QListThreadsInStopReply": {
1294 "cmd": cmd_query_packet
,
1295 "rsp": rsp_ok_means_supported
,
1296 "name": "query if threads in stop reply packets are supported",
1298 "QSetDetachOnError:": {
1299 "cmd": cmd_QSetWithUnsigned
,
1300 "rsp": rsp_ok_means_success
,
1301 "name": "set if we should detach on error",
1303 "QSetDisableASLR:": {
1304 "cmd": cmd_QSetWithUnsigned
,
1305 "rsp": rsp_ok_means_success
,
1306 "name": "set if we should disable ASLR",
1309 "cmd": cmd_query_packet
,
1310 "rsp": rsp_ok_means_success
,
1311 "name": "check on launch success for the A packet",
1313 "A": {"cmd": cmd_A
, "rsp": rsp_ok_means_success
, "name": "launch process"},
1315 "cmd": cmd_QSetWithString
,
1316 "rsp": rsp_ok_means_supported
,
1317 "name": "set the arch to launch in case the file contains multiple architectures",
1319 "qVAttachOrWaitSupported": {
1320 "cmd": cmd_query_packet
,
1321 "rsp": rsp_ok_means_supported
,
1322 "name": "set the launch architecture",
1325 "cmd": cmd_query_packet
,
1326 "rsp": rsp_dump_key_value_pairs
,
1327 "name": "get host information",
1329 "qC": {"cmd": cmd_qC
, "rsp": rsp_qC
, "name": "return the current thread ID"},
1330 "vCont": {"cmd": cmd_vCont
, "rsp": rsp_vCont
, "name": "extended continue command"},
1332 "cmd": cmd_qSpeedTest
,
1333 "rsp": rsp_qSpeedTest
,
1334 "name": "speed test packdet",
1336 "vAttach": {"cmd": cmd_vAttach
, "rsp": rsp_stop_reply
, "name": "attach to process"},
1337 "c": {"cmd": cmd_c
, "rsp": rsp_stop_reply
, "name": "continue"},
1338 "s": {"cmd": cmd_s
, "rsp": rsp_stop_reply
, "name": "step"},
1340 "cmd": cmd_qRegisterInfo
,
1341 "rsp": rsp_qRegisterInfo
,
1342 "name": "query register info",
1345 "cmd": cmd_qThreadInfo
,
1346 "rsp": rsp_qThreadInfo
,
1347 "name": "get current thread list",
1350 "cmd": cmd_qThreadInfo
,
1351 "rsp": rsp_qThreadInfo
,
1352 "name": "get current thread list",
1355 "cmd": cmd_query_packet
,
1356 "rsp": rsp_hex_big_endian
,
1357 "name": "get shared library info address",
1359 "qMemoryRegionInfo": {
1360 "cmd": cmd_mem_rgn_info
,
1361 "rsp": rsp_dump_key_value_pairs
,
1362 "name": "get memory region information",
1365 "cmd": cmd_query_packet
,
1366 "rsp": rsp_dump_key_value_pairs
,
1367 "name": "get process info",
1370 "cmd": cmd_query_packet
,
1371 "rsp": rsp_ok_means_supported
,
1372 "name": "query supported",
1374 "qXfer:": {"cmd": cmd_qXfer
, "rsp": rsp_qXfer
, "name": "qXfer"},
1375 "qSymbol:": {"cmd": cmd_qSymbol
, "rsp": rsp_qSymbol
, "name": "qSymbol"},
1377 "cmd": cmd_QSetWithHexString
,
1378 "rsp": rsp_ok_means_success
,
1379 "name": "set STDIN prior to launching with A packet",
1382 "cmd": cmd_QSetWithHexString
,
1383 "rsp": rsp_ok_means_success
,
1384 "name": "set STDOUT prior to launching with A packet",
1387 "cmd": cmd_QSetWithHexString
,
1388 "rsp": rsp_ok_means_success
,
1389 "name": "set STDERR prior to launching with A packet",
1392 "cmd": cmd_QSetWithString
,
1393 "rsp": rsp_ok_means_success
,
1394 "name": "set an environment variable prior to launching with A packet",
1396 "QEnvironmentHexEncoded:": {
1397 "cmd": cmd_QSetWithHexString
,
1398 "rsp": rsp_ok_means_success
,
1399 "name": "set an environment variable prior to launching with A packet",
1402 "cmd": cmd_read_mem_bin
,
1403 "rsp": rsp_mem_bin_bytes
,
1404 "name": "read memory binary",
1407 "cmd": cmd_write_memory
,
1408 "rsp": rsp_ok_means_success
,
1409 "name": "write memory binary",
1411 "m": {"cmd": cmd_read_memory
, "rsp": rsp_memory_bytes
, "name": "read memory"},
1412 "M": {"cmd": cmd_write_memory
, "rsp": rsp_ok_means_success
, "name": "write memory"},
1413 "_M": {"cmd": cmd_alloc_memory
, "rsp": rsp_alloc_memory
, "name": "allocate memory"},
1415 "cmd": cmd_dealloc_memory
,
1416 "rsp": rsp_ok_means_success
,
1417 "name": "deallocate memory",
1420 "cmd": cmd_read_one_reg
,
1421 "rsp": rsp_read_one_reg
,
1422 "name": "read single register",
1425 "cmd": cmd_write_one_reg
,
1426 "rsp": rsp_ok_means_success
,
1427 "name": "write single register",
1430 "cmd": cmd_read_all_regs
,
1431 "rsp": rsp_read_all_regs
,
1432 "name": "read all registers",
1435 "cmd": cmd_write_all_regs
,
1436 "rsp": rsp_ok_means_success
,
1437 "name": "write all registers",
1441 "rsp": rsp_ok_means_success
,
1442 "name": "clear breakpoint or watchpoint",
1446 "rsp": rsp_ok_means_success
,
1447 "name": "set breakpoint or watchpoint",
1449 "k": {"cmd": cmd_kill
, "rsp": rsp_stop_reply
, "name": "kill process"},
1451 "cmd": cmd_jThreadsInfo
,
1453 "name": "JSON get all threads info",
1455 "jGetLoadedDynamicLibrariesInfos:": {
1456 "cmd": cmd_jGetLoadedDynamicLibrariesInfos
,
1457 "rsp": rsp_jGetLoadedDynamicLibrariesInfos
,
1458 "name": "JSON get loaded dynamic libraries",
1463 def calculate_mean_and_standard_deviation(floats
):
1474 accum
+= delta
* delta
1476 std_dev
= math
.sqrt(accum
/ (count
- 1))
1477 return (mean
, std_dev
)
1480 def parse_gdb_log_file(path
, options
):
1482 parse_gdb_log(f
, options
)
1486 def round_up(n
, incr
):
1487 return float(((int(n
) + incr
) / incr
) * incr
)
1490 def plot_latencies(sec_times
):
1491 # import numpy as np
1492 import matplotlib
.pyplot
as plt
1494 for i
, name
in enumerate(sec_times
.keys()):
1495 times
= sec_times
[name
]
1498 plt
.subplot(2, 1, 1)
1499 plt
.title('Packet "%s" Times' % (name
))
1500 plt
.xlabel("Packet")
1505 time
= time
* 1000.0
1506 adj_times
.append(time
)
1512 for i
in range(len(adj_times
)):
1513 adj_times
[i
] *= 1000.0
1514 if adj_times
[i
] > max_time
:
1515 max_time
= adj_times
[i
]
1516 plt
.ylabel("Time (%s)" % (units
))
1518 for i
in [5.0, 10.0, 25.0, 50.0]:
1520 max_y
= round_up(max_time
, i
)
1523 max_y
= round_up(max_time
, 100.0)
1524 plt
.ylim(0.0, max_y
)
1525 plt
.plot(adj_times
, "o-")
1529 def parse_gdb_log(file, options
):
1530 """Parse a GDB log file that was generated by enabling logging with:
1531 (lldb) log enable --threadsafe --timestamp --file <FILE> gdb-remote packets
1532 This log file will contain timestamps and this function will then normalize
1533 those packets to be relative to the first value timestamp that is found and
1534 show delta times between log lines and also keep track of how long it takes
1535 for GDB remote commands to make a send/receive round trip. This can be
1536 handy when trying to figure out why some operation in the debugger is taking
1537 a long time during a preset set of debugger commands."""
1539 tricky_commands
= ["qRegisterInfo"]
1540 timestamp_regex
= re
.compile("(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$")
1541 packet_name_regex
= re
.compile("([A-Za-z_]+)[^a-z]")
1542 packet_transmit_name_regex
= re
.compile(
1543 "(?P<direction>send|read) packet: (?P<packet>.*)"
1545 packet_contents_name_regex
= re
.compile("\$([^#]*)#[0-9a-fA-F]{2}")
1546 packet_checksum_regex
= re
.compile(".*#[0-9a-fA-F]{2}$")
1547 packet_names_regex_str
= "(" + "|".join(gdb_remote_commands
.keys()) + ")(.*)"
1548 packet_names_regex
= re
.compile(packet_names_regex_str
)
1552 min_time
= 100000000.0
1553 packet_total_times
= {}
1554 all_packet_times
= []
1557 lines
= file.read().splitlines()
1559 last_command_args
= None
1560 last_command_packet
= None
1561 hide_next_response
= False
1562 num_lines
= len(lines
)
1564 for line_index
, line
in enumerate(lines
):
1565 # See if we need to skip any lines
1569 m
= packet_transmit_name_regex
.search(line
)
1573 direction
= m
.group("direction")
1574 is_command
= direction
== "send"
1575 packet
= m
.group("packet")
1576 sys
.stdout
.write(options
.colors
.green())
1577 if not options
.quiet
and not hide_next_response
:
1579 sys
.stdout
.write(options
.colors
.reset())
1581 # print 'direction = "%s", packet = "%s"' % (direction, packet)
1583 if packet
[0] == "+":
1585 print("-->", end
=" ")
1587 print("<--", end
=" ")
1588 if not options
.quiet
:
1591 elif packet
[0] == "-":
1593 print("-->", end
=" ")
1595 print("<--", end
=" ")
1596 if not options
.quiet
:
1599 elif packet
[0] == "$":
1600 m
= packet_contents_name_regex
.match(packet
)
1601 if not m
and packet
[0] == "$":
1602 multiline_packet
= packet
1603 idx
= line_index
+ 1
1604 while idx
< num_lines
:
1605 if not options
.quiet
and not hide_next_response
:
1606 print("# ", lines
[idx
])
1607 multiline_packet
+= lines
[idx
]
1608 m
= packet_contents_name_regex
.match(multiline_packet
)
1610 packet
= multiline_packet
1611 skip_count
= idx
- line_index
1617 print("-->", end
=" ")
1619 print("<--", end
=" ")
1620 contents
= decode_packet(m
.group(1))
1622 hide_next_response
= False
1623 m
= packet_names_regex
.match(contents
)
1625 last_command
= m
.group(1)
1626 if last_command
== "?":
1627 last_command
= "\\?"
1628 packet_name
= last_command
1629 last_command_args
= m
.group(2)
1630 last_command_packet
= contents
1631 hide_next_response
= gdb_remote_commands
[last_command
][
1633 ](options
, last_command
, last_command_args
)
1635 packet_match
= packet_name_regex
.match(contents
)
1637 packet_name
= packet_match
.group(1)
1638 for tricky_cmd
in tricky_commands
:
1639 if packet_name
.find(tricky_cmd
) == 0:
1640 packet_name
= tricky_cmd
1642 packet_name
= contents
1644 last_command_args
= None
1645 last_command_packet
= None
1647 gdb_remote_commands
[last_command
]["rsp"](
1648 options
, last_command
, last_command_args
, contents
1651 print('error: invalid packet: "', packet
, '"')
1656 match
= timestamp_regex
.match(line
)
1658 curr_time
= float(match
.group(2))
1659 if last_time
and not is_command
:
1660 delta
= curr_time
- last_time
1661 all_packet_times
.append(delta
)
1664 delta
= curr_time
- last_time
1666 base_time
= curr_time
1669 if line
.find("read packet: $") >= 0 and packet_name
:
1670 if packet_name
in packet_total_times
:
1671 packet_total_times
[packet_name
] += delta
1672 packet_counts
[packet_name
] += 1
1674 packet_total_times
[packet_name
] = delta
1675 packet_counts
[packet_name
] = 1
1676 if packet_name
not in packet_times
:
1677 packet_times
[packet_name
] = []
1678 packet_times
[packet_name
].append(delta
)
1680 if min_time
> delta
:
1683 if not options
or not options
.quiet
:
1686 % (match
.group(1), curr_time
- base_time
, delta
, match
.group(3))
1688 last_time
= curr_time
1691 (average
, std_dev
) = calculate_mean_and_standard_deviation(all_packet_times
)
1692 if average
and std_dev
:
1694 "%u packets with average packet time of %f and standard deviation of %f"
1695 % (len(all_packet_times
), average
, std_dev
)
1697 if packet_total_times
:
1698 total_packet_time
= 0.0
1699 total_packet_count
= 0
1700 for key
, vvv
in packet_total_times
.items():
1701 # print ' key = (%s) "%s"' % (type(key), key)
1702 # print 'value = (%s) %s' % (type(vvv), vvv)
1703 # if type(vvv) == 'float':
1704 total_packet_time
+= vvv
1705 for key
, vvv
in packet_counts
.items():
1706 total_packet_count
+= vvv
1708 print("#------------------------------------------------------------")
1709 print("# Packet timing summary:")
1711 "# Totals: time = %6f, count = %6d"
1712 % (total_packet_time
, total_packet_count
)
1714 print("# Min packet time: time = %6f" % (min_time
))
1715 print("#------------------------------------------------------------")
1716 print("# Packet Time (sec) Percent Count Latency")
1717 print("#------------------------- ----------- ------- ------ -------")
1718 if options
and options
.sort_count
:
1719 res
= sorted(packet_counts
, key
=packet_counts
.__getitem
__, reverse
=True)
1722 packet_total_times
, key
=packet_total_times
.__getitem
__, reverse
=True
1727 packet_total_time
= packet_total_times
[item
]
1728 packet_percent
= (packet_total_time
/ total_packet_time
) * 100.0
1729 packet_count
= packet_counts
[item
]
1731 " %24s %11.6f %5.2f%% %6d %9.6f"
1737 float(packet_total_time
) / float(packet_count
),
1740 if options
and options
.plot
:
1741 plot_latencies(packet_times
)
1744 if __name__
== "__main__":
1745 usage
= "usage: gdbremote [options]"
1746 description
= """The command disassembles a GDB remote packet log."""
1747 parser
= optparse
.OptionParser(
1748 description
=description
, prog
="gdbremote", usage
=usage
1753 action
="store_true",
1755 help="display verbose debug info",
1761 action
="store_true",
1763 help="display verbose debug info",
1769 action
="store_true",
1771 help="add terminal colors",
1777 action
="store_true",
1779 help="display verbose debug info",
1786 help="symbolicate using a darwin crash log file",
1790 (options
, args
) = parser
.parse_args(sys
.argv
[1:])
1792 print("error: argument error")
1795 options
.colors
= TerminalColors(options
.color
)
1796 options
.symbolicator
= None
1797 if options
.crashlog
:
1800 lldb
.debugger
= lldb
.SBDebugger
.Create()
1801 import lldb
.macosx
.crashlog
1803 options
.symbolicator
= lldb
.macosx
.crashlog
.CrashLog(options
.crashlog
)
1804 print("%s" % (options
.symbolicator
))
1806 # This script is being run from the command line, create a debugger in case we are
1807 # going to use any debugger functions in our function.
1811 "#----------------------------------------------------------------------"
1813 print("# GDB remote log file: '%s'" % file)
1815 "#----------------------------------------------------------------------"
1817 parse_gdb_log_file(file, options
)
1818 if options
.symbolicator
:
1819 print("%s" % (options
.symbolicator
))
1821 parse_gdb_log(sys
.stdin
, options
)
1824 def __lldb_init_module(debugger
, internal_dict
):
1825 # This initializer is being run from LLDB in the embedded command interpreter
1826 # Add any commands contained in this module to LLDB
1827 debugger
.HandleCommand(
1828 "command script add -o -f gdbremote.start_gdb_log start_gdb_log"
1830 debugger
.HandleCommand(
1831 "command script add -o -f gdbremote.stop_gdb_log stop_gdb_log"
1834 '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'