[ELF] Reorder SectionBase/InputSectionBase members
[llvm-project.git] / lldb / examples / python / gdbremote.py
blob40ee15853fdb217b1e4bb6bab83a2996307915ad
1 #!/usr/bin/env python
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
8 # will primnt ou
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
16 # available.
17 # ----------------------------------------------------------------------
19 import binascii
20 import subprocess
21 import json
22 import math
23 import optparse
24 import os
25 import re
26 import shlex
27 import string
28 import sys
29 import tempfile
30 import xml.etree.ElementTree as ET
32 # ----------------------------------------------------------------------
33 # Global variables
34 # ----------------------------------------------------------------------
35 g_log_file = ""
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)")
41 class TerminalColors:
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
49 def reset(self):
50 """Reset all terminal colors and formatting."""
51 if self.enabled:
52 return "\x1b[0m"
53 return ""
55 def bold(self, on=True):
56 """Enable or disable bold depending on the "on" parameter."""
57 if self.enabled:
58 if on:
59 return "\x1b[1m"
60 else:
61 return "\x1b[22m"
62 return ""
64 def italics(self, on=True):
65 """Enable or disable italics depending on the "on" parameter."""
66 if self.enabled:
67 if on:
68 return "\x1b[3m"
69 else:
70 return "\x1b[23m"
71 return ""
73 def underline(self, on=True):
74 """Enable or disable underline depending on the "on" parameter."""
75 if self.enabled:
76 if on:
77 return "\x1b[4m"
78 else:
79 return "\x1b[24m"
80 return ""
82 def inverse(self, on=True):
83 """Enable or disable inverse depending on the "on" parameter."""
84 if self.enabled:
85 if on:
86 return "\x1b[7m"
87 else:
88 return "\x1b[27m"
89 return ""
91 def strike(self, on=True):
92 """Enable or disable strike through depending on the "on" parameter."""
93 if self.enabled:
94 if on:
95 return "\x1b[9m"
96 else:
97 return "\x1b[29m"
98 return ""
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.
104 if self.enabled:
105 if fg:
106 return "\x1b[30m"
107 else:
108 return "\x1b[40m"
109 return ""
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.
115 if self.enabled:
116 if fg:
117 return "\x1b[31m"
118 else:
119 return "\x1b[41m"
120 return ""
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.
126 if self.enabled:
127 if fg:
128 return "\x1b[32m"
129 else:
130 return "\x1b[42m"
131 return ""
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.
137 if self.enabled:
138 if fg:
139 return "\x1b[33m"
140 else:
141 return "\x1b[43m"
142 return ""
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.
148 if self.enabled:
149 if fg:
150 return "\x1b[34m"
151 else:
152 return "\x1b[44m"
153 return ""
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.
159 if self.enabled:
160 if fg:
161 return "\x1b[35m"
162 else:
163 return "\x1b[45m"
164 return ""
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.
170 if self.enabled:
171 if fg:
172 return "\x1b[36m"
173 else:
174 return "\x1b[46m"
175 return ""
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.
181 if self.enabled:
182 if fg:
183 return "\x1b[37m"
184 else:
185 return "\x1b[47m"
186 return ""
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.
192 if self.enabled:
193 if fg:
194 return "\x1b[39m"
195 else:
196 return "\x1b[49m"
197 return ""
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."""
204 global g_log_file
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
212 parser.add_option(
213 "-v",
214 "--verbose",
215 action="store_true",
216 dest="verbose",
217 help="display verbose debug info",
218 default=False,
220 try:
221 (options, args) = parser.parse_args(command_args)
222 except:
223 return
225 if g_log_file:
226 result.PutCString(
227 'error: logging is already in progress with file "%s"' % g_log_file
229 else:
230 args_len = len(args)
231 if args_len == 0:
232 g_log_file = tempfile.mktemp()
233 elif len(args) == 1:
234 g_log_file = args[0]
236 if g_log_file:
237 debugger.HandleCommand(
238 'log enable --threadsafe --timestamp --file "%s" gdb-remote packets'
239 % g_log_file
241 result.PutCString(
242 "GDB packet logging enable with log file '%s'\nUse the 'stop_gdb_log' command to stop logging and show packet statistics."
243 % g_log_file
245 return
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."""
256 global g_log_file
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
265 parser.add_option(
266 "-v",
267 "--verbose",
268 action="store_true",
269 dest="verbose",
270 help="display verbose debug info",
271 default=False,
273 parser.add_option(
274 "--plot",
275 action="store_true",
276 dest="plot",
277 help="plot packet latencies by packet type",
278 default=False,
280 parser.add_option(
281 "-q",
282 "--quiet",
283 action="store_true",
284 dest="quiet",
285 help="display verbose debug info",
286 default=False,
288 parser.add_option(
289 "-C",
290 "--color",
291 action="store_true",
292 dest="color",
293 help="add terminal colors",
294 default=False,
296 parser.add_option(
297 "-c",
298 "--sort-by-count",
299 action="store_true",
300 dest="sort_count",
301 help="display verbose debug info",
302 default=False,
304 parser.add_option(
305 "-s",
306 "--symbolicate",
307 action="store_true",
308 dest="symbolicate",
309 help='symbolicate addresses in log using current "lldb.target"',
310 default=False,
312 try:
313 (options, args) = parser.parse_args(command_args)
314 except:
315 return
316 options.colors = TerminalColors(options.color)
317 options.symbolicator = None
318 if options.symbolicate:
319 if lldb.target:
320 import lldb.utils.symbolication
322 options.symbolicator = lldb.utils.symbolication.Symbolicator()
323 options.symbolicator.target = lldb.target
324 else:
325 print("error: can't symbolicate without a target")
327 if not g_log_file:
328 result.PutCString(
329 'error: logging must have been previously enabled with a call to "stop_gdb_log"'
331 elif os.path.exists(g_log_file):
332 if len(args) == 0:
333 debugger.HandleCommand("log disable gdb-remote packets")
334 result.PutCString(
335 "GDB packet logging disabled. Logged packets are in '%s'" % g_log_file
337 parse_gdb_log_file(g_log_file, options)
338 else:
339 result.PutCString(usage)
340 else:
341 print('error: the GDB packet log file "%s" does not exist' % g_log_file)
344 def is_hex_byte(str):
345 if len(str) == 2:
346 return str[0] in string.hexdigits and str[1] in string.hexdigits
347 return False
350 def get_hex_string_if_all_printable(str):
351 try:
352 s = binascii.unhexlify(str).decode()
353 if all(c in string.printable for c in s):
354 return s
355 except (TypeError, binascii.Error, UnicodeDecodeError):
356 pass
357 return None
360 # global register info list
361 g_register_infos = list()
362 g_max_register_info_name_len = 0
365 class RegisterInfo:
366 """Class that represents register information"""
368 def __init__(self, kvp):
369 self.info = dict()
370 for kv in kvp:
371 key = kv[0]
372 value = kv[1]
373 self.info[key] = value
375 def name(self):
376 """Get the name of the register."""
377 if self.info and "name" in self.info:
378 return self.info["name"]
379 return None
381 def bit_size(self):
382 """Get the size in bits of the register."""
383 if self.info and "bitsize" in self.info:
384 return int(self.info["bitsize"])
385 return 0
387 def byte_size(self):
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)
398 if bit_size == 8:
399 return "0x%2.2x" % (uval)
400 elif bit_size == 16:
401 return "0x%4.4x" % (uval)
402 elif bit_size == 32:
403 return "0x%8.8x" % (uval)
404 elif bit_size == 64:
405 return "0x%16.16x" % (uval)
406 bytes = list()
407 uval = packet.get_hex_uint8()
408 while uval is not None:
409 bytes.append(uval)
410 uval = packet.get_hex_uint8()
411 value_str = "0x"
412 if g_byte_order == "little":
413 bytes.reverse()
414 for byte in bytes:
415 value_str += "%2.2x" % byte
416 return "%s" % (value_str)
418 def __str__(self):
419 """Dump the register info key/value pairs"""
420 s = ""
421 for key in self.info.keys():
422 if s:
423 s += ", "
424 s += "%s=%s " % (key, self.info[key])
425 return s
428 class Packet:
429 """Class that represents a packet that contains string data"""
431 def __init__(self, packet_str):
432 self.str = packet_str
434 def peek_char(self):
435 ch = 0
436 if self.str:
437 ch = self.str[0]
438 return ch
440 def get_char(self):
441 ch = 0
442 if self.str:
443 ch = self.str[0]
444 self.str = self.str[1:]
445 return ch
447 def skip_exact_string(self, s):
448 if self.str and self.str.startswith(s):
449 self.str = self.str[len(s) :]
450 return True
451 else:
452 return False
454 def get_thread_id(self, fail_value=-1):
455 match = g_number_regex.match(self.str)
456 if match:
457 number_str = match.group(1)
458 self.str = self.str[len(number_str) :]
459 return int(number_str, 0)
460 else:
461 return fail_value
463 def get_hex_uint8(self):
464 if (
465 self.str
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:]
472 return uval
473 return None
475 def get_hex_uint16(self, byte_order):
476 uval = 0
477 if byte_order == "big":
478 uval |= self.get_hex_uint8() << 8
479 uval |= self.get_hex_uint8()
480 else:
481 uval |= self.get_hex_uint8()
482 uval |= self.get_hex_uint8() << 8
483 return uval
485 def get_hex_uint32(self, byte_order):
486 uval = 0
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()
492 else:
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
497 return uval
499 def get_hex_uint64(self, byte_order):
500 uval = 0
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()
510 else:
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
519 return uval
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)
526 if match:
527 number_str = match.group(1)
528 self.str = self.str[len(number_str) :]
529 return int(number_str, 0)
530 else:
531 return fail_value
533 def get_hex_ascii_str(self, n=0):
534 hex_chars = self.get_hex_chars(n)
535 if hex_chars:
536 return binascii.unhexlify(hex_chars)
537 else:
538 return None
540 def get_hex_chars(self, n=0):
541 str_len = len(self.str)
542 if n == 0:
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:
546 n = n + 1
547 else:
548 if n > str_len:
549 return None # Not enough chars
550 # Verify all chars are hex if a length was specified
551 for i in range(n):
552 if self.str[i] not in string.hexdigits:
553 return None # Not all hex digits
554 if n == 0:
555 return None
556 hex_str = self.str[0:n]
557 self.str = self.str[n:]
558 return hex_str
560 def get_hex_uint(self, byte_order, n=0):
561 if byte_order == "big":
562 hex_str = self.get_hex_chars(n)
563 if hex_str is None:
564 return None
565 return int(hex_str, 16)
566 else:
567 uval = self.get_hex_uint8()
568 if uval is None:
569 return None
570 uval_result = 0
571 shift = 0
572 while uval is not None:
573 uval_result |= uval << shift
574 shift += 8
575 uval = self.get_hex_uint8()
576 return uval_result
578 def get_key_value_pairs(self):
579 kvp = list()
580 if ";" in self.str:
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))
585 return kvp
587 def split(self, ch):
588 return self.str.split(ch)
590 def split_hex(self, ch, byte_order):
591 hex_values = list()
592 strings = self.str.split(ch)
593 for str in strings:
594 hex_values.append(Packet(str).get_hex_uint(byte_order))
595 return hex_values
597 def __str__(self):
598 return self.str
600 def __len__(self):
601 return len(self.str)
604 g_thread_suffix_regex = re.compile(";thread:([0-9a-fA-F]+);")
607 def get_thread_from_thread_suffix(str):
608 if str:
609 match = g_thread_suffix_regex.match(str)
610 if match:
611 return int(match.group(1), 16)
612 return None
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()")
623 return False
626 def rsp_stop_reply(options, cmd, cmd_args, rsp):
627 global g_byte_order
628 packet = Packet(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]
635 if is_hex_byte(key):
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(
641 key_value_pair[1]
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):
656 if args:
657 print("cmd: %s, args: %s", cmd, args)
658 else:
659 print("cmd: %s", cmd)
660 return False
663 def cmd_qSymbol(options, cmd, args):
664 if args == ":":
665 print("ready to serve symbols")
666 else:
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))
673 else:
674 print("error: bad command format")
675 else:
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))
679 else:
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):
696 if len(rsp) == 0:
697 print("Unsupported")
698 else:
699 if rsp == "OK":
700 print("No more symbols to lookup")
701 else:
702 packet = Packet(rsp)
703 if packet.skip_exact_string("qSymbol:"):
704 symbol_name = packet.get_hex_ascii_str()
705 print('lookup_symbol("%s")' % (symbol_name))
706 else:
707 print(
708 'error: response string should start with "qSymbol:": respnse is "%s"'
709 % (rsp)
713 def cmd_qXfer(options, cmd, args):
714 # $qXfer:features:read:target.xml:0,1ffff#14
715 print("read target special data %s" % (args))
716 return True
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()
727 if xml_string:
728 ch = xml_string[0]
729 if ch == "l":
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"]
737 else:
738 reg_info.info["name"] = "unspecified"
739 if "encoding" in reg_element.attrib:
740 reg_info.info["encoding"] = reg_element.attrib[
741 "encoding"
743 else:
744 reg_info.info["encoding"] = "uint"
745 if "offset" in reg_element.attrib:
746 reg_info.info["offset"] = reg_element.attrib[
747 "offset"
749 if "bitsize" in reg_element.attrib:
750 reg_info.info["bitsize"] = reg_element.attrib[
751 "bitsize"
753 g_register_infos.append(reg_info)
754 print('XML for "%s":' % (data[2]))
755 ET.dump(xml_root)
758 def cmd_A(options, cmd, args):
759 print("launch process:")
760 packet = Packet(args)
761 while True:
762 arg_len = packet.get_number()
763 if arg_len == -1:
764 break
765 if not packet.skip_exact_string(","):
766 break
767 arg_idx = packet.get_number()
768 if arg_idx == -1:
769 break
770 if not packet.skip_exact_string(","):
771 break
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):
781 packet = Packet(rsp)
782 if packet.skip_exact_string("QC"):
783 tid = packet.get_thread_id()
784 print("current_thread_id = %#x" % (tid))
785 else:
786 print("current_thread_id = old thread ID")
789 def cmd_query_packet(options, cmd, args):
790 if args:
791 print("%s%s" % (cmd, args))
792 else:
793 print("%s" % (cmd))
794 return False
797 def rsp_ok_error(rsp):
798 print("rsp: ", rsp)
801 def rsp_ok_means_supported(options, cmd, cmd_args, rsp):
802 if rsp == "OK":
803 print("%s%s is supported" % (cmd, cmd_args))
804 elif rsp == "":
805 print("%s%s is not supported" % (cmd, cmd_args))
806 else:
807 print("%s%s -> %s" % (cmd, cmd_args, rsp))
810 def rsp_ok_means_success(options, cmd, cmd_args, rsp):
811 if rsp == "OK":
812 print("success")
813 elif rsp == "":
814 print("%s%s is not supported" % (cmd, cmd_args))
815 else:
816 print("%s%s -> %s" % (cmd, cmd_args, rsp))
819 def dump_key_value_pairs(key_value_pairs):
820 max_key_len = 0
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)
829 if unhex_value:
830 print("%*s = %s (%s)" % (max_key_len, key, value, unhex_value))
831 else:
832 print("%*s = %s" % (max_key_len, key, value))
835 def rsp_dump_key_value_pairs(options, cmd, cmd_args, rsp):
836 if rsp:
837 print("%s response:" % (cmd))
838 packet = Packet(rsp)
839 key_value_pairs = packet.get_key_value_pairs()
840 dump_key_value_pairs(key_value_pairs)
841 else:
842 print("not supported")
845 def cmd_c(options, cmd, args):
846 print("continue()")
847 return False
850 def cmd_s(options, cmd, args):
851 print("step()")
852 return False
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):
864 if args == "?":
865 print("%s: get supported extended continue modes" % (cmd))
866 else:
867 got_other_threads = 0
868 s = ""
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":
873 action = "continue"
874 elif short_action == "s":
875 action = "step"
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:])
880 else:
881 action = short_action
882 if s:
883 s += ", "
884 if tid == -1:
885 got_other_threads = 1
886 s += "other-threads:"
887 else:
888 s += "thread 0x%4.4x: %s" % (tid, action)
889 if got_other_threads:
890 print("extended_continue (%s)" % (s))
891 else:
892 print("extended_continue (%s, other-threads: suspend)" % (s))
893 return False
896 def rsp_vCont(options, cmd, cmd_args, rsp):
897 if cmd_args == "?":
898 # Skip the leading 'vCont;'
899 rsp = rsp[6:]
900 modes = rsp.split(";")
901 s = "%s: supported extended continue modes include: " % (cmd)
903 for i, mode in enumerate(modes):
904 if i:
905 s += ", "
906 if mode == "c":
907 s += "continue"
908 elif mode == "C":
909 s += "continue with signal"
910 elif mode == "s":
911 s += "step"
912 elif mode == "S":
913 s += "step with signal"
914 elif mode == "t":
915 s += "stop"
916 # else:
917 # s += 'unrecognized vCont mode: ', str(mode)
918 print(s)
919 elif rsp:
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)
922 return
923 if rsp[0] == "O":
924 print("stdout: %s" % (rsp))
925 return
926 else:
927 print(
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(";")
934 if extra_command:
935 print("%s%s(%s)" % (cmd, extra_command, args))
936 else:
937 print("attach(pid = %u)" % int(args, 16))
938 return False
941 def cmd_qRegisterInfo(options, cmd, args):
942 print("query_register_info(reg_num=%i)" % (int(args, 16)))
943 return False
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
955 print(" DONE")
956 else:
957 packet = Packet(rsp)
958 reg_info = RegisterInfo(packet.get_key_value_pairs())
959 g_register_infos.append(reg_info)
960 print(reg_info)
961 return False
964 def cmd_qThreadInfo(options, cmd, args):
965 if cmd == "qfThreadInfo":
966 query_type = "first"
967 else:
968 query_type = "subsequent"
969 print("get_current_thread_list(type=%s)" % (query_type))
970 return False
973 def rsp_qThreadInfo(options, cmd, cmd_args, rsp):
974 packet = Packet(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):
979 if i:
980 print(",", end=" ")
981 print("0x%x" % (tid), end=" ")
982 print()
983 elif response_type == "l":
984 print("END")
987 def rsp_hex_big_endian(options, cmd, cmd_args, rsp):
988 if rsp == "":
989 print("%s%s is not supported" % (cmd, cmd_args))
990 else:
991 packet = Packet(rsp)
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))
1003 return False
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")
1011 print("memory:")
1012 if size > 0:
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))
1022 return False
1025 def dump_hex_memory_buffer(addr, hex_byte_str):
1026 packet = Packet(hex_byte_str)
1027 idx = 0
1028 ascii = ""
1029 uval = packet.get_hex_uint8()
1030 while uval is not None:
1031 if (idx % 16) == 0:
1032 if ascii:
1033 print(" ", ascii)
1034 ascii = ""
1035 print("0x%x:" % (addr + idx), end=" ")
1036 print("%2.2x" % (uval), end=" ")
1037 if 0x20 <= uval and uval < 0x7F:
1038 ascii += "%c" % uval
1039 else:
1040 ascii += "."
1041 uval = packet.get_hex_uint8()
1042 idx = idx + 1
1043 if ascii:
1044 print(" ", ascii)
1045 ascii = ""
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)")
1053 return
1054 size = packet.get_hex_uint("big")
1055 if packet.get_char() != ":":
1056 print("error: invalid write memory command (missing colon after size)")
1057 return
1058 print("write_memory (addr = 0x%16.16x, size = %u, data:" % (addr, size))
1059 dump_hex_memory_buffer(addr, packet.str)
1060 return False
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)")
1068 return
1069 print(
1070 "allocate_memory (byte-size = %u (0x%x), permissions = %s)"
1071 % (byte_size, byte_size, packet.str)
1073 return False
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)")
1087 else:
1088 print("deallocate_memory (addr = 0x%x, permissions = %s)" % (addr, packet.str))
1089 return False
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()
1108 return s
1109 s += value_str
1110 return s
1111 else:
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)
1120 name = None
1121 if reg_num < len(g_register_infos):
1122 name = g_register_infos[reg_num].name()
1123 if packet.str:
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
1128 if name:
1129 s += " (%s)" % (name)
1130 if tid is not None:
1131 s += ", tid = 0x%4.4x" % (tid)
1132 s += ")"
1133 print(s)
1134 return False
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")
1148 else:
1149 name = None
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
1153 if name:
1154 s += " (%s)" % (name)
1155 s += ", value = "
1156 s += get_register_name_equal_value(options, reg_num, hex_value_str)
1157 if tid is not None:
1158 s += ", tid = 0x%4.4x" % (tid)
1159 s += ")"
1160 print(s)
1161 return False
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))
1171 else:
1172 return
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)
1179 if tid is not None:
1180 print("read_all_register(thread = 0x%4.4x)" % tid)
1181 else:
1182 print("read_all_register()")
1183 return False
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)
1195 return False
1198 g_bp_types = ["software_bp", "hardware_bp", "write_wp", "read_wp", "access_wp"]
1201 def cmd_bp(options, cmd, args):
1202 if cmd == "Z":
1203 s = "set_"
1204 else:
1205 s = "clear_"
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)
1214 print(s)
1215 return False
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))
1223 return False
1226 def cmd_kill(options, cmd, args):
1227 print("kill_process()")
1228 return False
1231 def cmd_jThreadsInfo(options, cmd, args):
1232 print("jThreadsInfo()")
1233 return False
1236 def cmd_jGetLoadedDynamicLibrariesInfos(options, cmd, args):
1237 print("jGetLoadedDynamicLibrariesInfos()")
1238 return False
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
1245 if have_escapes:
1246 normal_s = s[start_index:index]
1247 else:
1248 normal_s = s[start_index:]
1249 # print 'normal_s = "%s"' % (normal_s)
1250 if have_escapes:
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)
1254 else:
1255 return normal_s
1258 def rsp_json(options, cmd, cmd_args, rsp):
1259 print("%s() reply:" % (cmd))
1260 if not rsp:
1261 return
1262 try:
1263 json_tree = json.loads(rsp)
1264 print(json.dumps(json_tree, indent=4, separators=(",", ": ")))
1265 except json.JSONDecodeError:
1266 return
1269 def rsp_jGetLoadedDynamicLibrariesInfos(options, cmd, cmd_args, rsp):
1270 if cmd_args:
1271 rsp_json(options, cmd, cmd_args, rsp)
1272 else:
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",
1308 "qLaunchSuccess": {
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"},
1314 "QLaunchArch:": {
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",
1324 "qHostInfo": {
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"},
1331 "qSpeedTest": {
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"},
1339 "qRegisterInfo": {
1340 "cmd": cmd_qRegisterInfo,
1341 "rsp": rsp_qRegisterInfo,
1342 "name": "query register info",
1344 "qfThreadInfo": {
1345 "cmd": cmd_qThreadInfo,
1346 "rsp": rsp_qThreadInfo,
1347 "name": "get current thread list",
1349 "qsThreadInfo": {
1350 "cmd": cmd_qThreadInfo,
1351 "rsp": rsp_qThreadInfo,
1352 "name": "get current thread list",
1354 "qShlibInfoAddr": {
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",
1364 "qProcessInfo": {
1365 "cmd": cmd_query_packet,
1366 "rsp": rsp_dump_key_value_pairs,
1367 "name": "get process info",
1369 "qSupported": {
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"},
1376 "QSetSTDIN:": {
1377 "cmd": cmd_QSetWithHexString,
1378 "rsp": rsp_ok_means_success,
1379 "name": "set STDIN prior to launching with A packet",
1381 "QSetSTDOUT:": {
1382 "cmd": cmd_QSetWithHexString,
1383 "rsp": rsp_ok_means_success,
1384 "name": "set STDOUT prior to launching with A packet",
1386 "QSetSTDERR:": {
1387 "cmd": cmd_QSetWithHexString,
1388 "rsp": rsp_ok_means_success,
1389 "name": "set STDERR prior to launching with A packet",
1391 "QEnvironment:": {
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",
1401 "x": {
1402 "cmd": cmd_read_mem_bin,
1403 "rsp": rsp_mem_bin_bytes,
1404 "name": "read memory binary",
1406 "X": {
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"},
1414 "_m": {
1415 "cmd": cmd_dealloc_memory,
1416 "rsp": rsp_ok_means_success,
1417 "name": "deallocate memory",
1419 "p": {
1420 "cmd": cmd_read_one_reg,
1421 "rsp": rsp_read_one_reg,
1422 "name": "read single register",
1424 "P": {
1425 "cmd": cmd_write_one_reg,
1426 "rsp": rsp_ok_means_success,
1427 "name": "write single register",
1429 "g": {
1430 "cmd": cmd_read_all_regs,
1431 "rsp": rsp_read_all_regs,
1432 "name": "read all registers",
1434 "G": {
1435 "cmd": cmd_write_all_regs,
1436 "rsp": rsp_ok_means_success,
1437 "name": "write all registers",
1439 "z": {
1440 "cmd": cmd_bp,
1441 "rsp": rsp_ok_means_success,
1442 "name": "clear breakpoint or watchpoint",
1444 "Z": {
1445 "cmd": cmd_bp,
1446 "rsp": rsp_ok_means_success,
1447 "name": "set breakpoint or watchpoint",
1449 "k": {"cmd": cmd_kill, "rsp": rsp_stop_reply, "name": "kill process"},
1450 "jThreadsInfo": {
1451 "cmd": cmd_jThreadsInfo,
1452 "rsp": rsp_json,
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):
1464 sum = 0.0
1465 count = len(floats)
1466 if count == 0:
1467 return (0.0, 0.0)
1468 for f in floats:
1469 sum += f
1470 mean = sum / count
1471 accum = 0.0
1472 for f in floats:
1473 delta = f - mean
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):
1481 f = open(path)
1482 parse_gdb_log(f, options)
1483 f.close()
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]
1496 if len(times) <= 1:
1497 continue
1498 plt.subplot(2, 1, 1)
1499 plt.title('Packet "%s" Times' % (name))
1500 plt.xlabel("Packet")
1501 units = "ms"
1502 adj_times = []
1503 max_time = 0.0
1504 for time in times:
1505 time = time * 1000.0
1506 adj_times.append(time)
1507 if time > max_time:
1508 max_time = time
1509 if max_time < 1.0:
1510 units = "us"
1511 max_time = 0.0
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))
1517 max_y = None
1518 for i in [5.0, 10.0, 25.0, 50.0]:
1519 if max_time < i:
1520 max_y = round_up(max_time, i)
1521 break
1522 if max_y is None:
1523 max_y = round_up(max_time, 100.0)
1524 plt.ylim(0.0, max_y)
1525 plt.plot(adj_times, "o-")
1526 plt.show()
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)
1550 base_time = 0.0
1551 last_time = 0.0
1552 min_time = 100000000.0
1553 packet_total_times = {}
1554 all_packet_times = []
1555 packet_times = {}
1556 packet_counts = {}
1557 lines = file.read().splitlines()
1558 last_command = None
1559 last_command_args = None
1560 last_command_packet = None
1561 hide_next_response = False
1562 num_lines = len(lines)
1563 skip_count = 0
1564 for line_index, line in enumerate(lines):
1565 # See if we need to skip any lines
1566 if skip_count > 0:
1567 skip_count -= 1
1568 continue
1569 m = packet_transmit_name_regex.search(line)
1570 is_command = False
1571 direction = None
1572 if m:
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:
1578 print("# ", line)
1579 sys.stdout.write(options.colors.reset())
1581 # print 'direction = "%s", packet = "%s"' % (direction, packet)
1583 if packet[0] == "+":
1584 if is_command:
1585 print("-->", end=" ")
1586 else:
1587 print("<--", end=" ")
1588 if not options.quiet:
1589 print("ACK")
1590 continue
1591 elif packet[0] == "-":
1592 if is_command:
1593 print("-->", end=" ")
1594 else:
1595 print("<--", end=" ")
1596 if not options.quiet:
1597 print("NACK")
1598 continue
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)
1609 if m:
1610 packet = multiline_packet
1611 skip_count = idx - line_index
1612 break
1613 else:
1614 idx += 1
1615 if m:
1616 if is_command:
1617 print("-->", end=" ")
1618 else:
1619 print("<--", end=" ")
1620 contents = decode_packet(m.group(1))
1621 if is_command:
1622 hide_next_response = False
1623 m = packet_names_regex.match(contents)
1624 if m:
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][
1632 "cmd"
1633 ](options, last_command, last_command_args)
1634 else:
1635 packet_match = packet_name_regex.match(contents)
1636 if packet_match:
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
1641 else:
1642 packet_name = contents
1643 last_command = None
1644 last_command_args = None
1645 last_command_packet = None
1646 elif last_command:
1647 gdb_remote_commands[last_command]["rsp"](
1648 options, last_command, last_command_args, contents
1650 else:
1651 print('error: invalid packet: "', packet, '"')
1652 else:
1653 print("???")
1654 else:
1655 print("## ", line)
1656 match = timestamp_regex.match(line)
1657 if match:
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)
1662 delta = 0.0
1663 if base_time:
1664 delta = curr_time - last_time
1665 else:
1666 base_time = curr_time
1668 if not is_command:
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
1673 else:
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)
1679 packet_name = None
1680 if min_time > delta:
1681 min_time = delta
1683 if not options or not options.quiet:
1684 print(
1685 "%s%.6f %+.6f%s"
1686 % (match.group(1), curr_time - base_time, delta, match.group(3))
1688 last_time = curr_time
1689 # else:
1690 # print line
1691 (average, std_dev) = calculate_mean_and_standard_deviation(all_packet_times)
1692 if average and std_dev:
1693 print(
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:")
1710 print(
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)
1720 else:
1721 res = sorted(
1722 packet_total_times, key=packet_total_times.__getitem__, reverse=True
1725 if last_time > 0.0:
1726 for item in res:
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]
1730 print(
1731 " %24s %11.6f %5.2f%% %6d %9.6f"
1733 item,
1734 packet_total_time,
1735 packet_percent,
1736 packet_count,
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
1750 parser.add_option(
1751 "-v",
1752 "--verbose",
1753 action="store_true",
1754 dest="verbose",
1755 help="display verbose debug info",
1756 default=False,
1758 parser.add_option(
1759 "-q",
1760 "--quiet",
1761 action="store_true",
1762 dest="quiet",
1763 help="display verbose debug info",
1764 default=False,
1766 parser.add_option(
1767 "-C",
1768 "--color",
1769 action="store_true",
1770 dest="color",
1771 help="add terminal colors",
1772 default=False,
1774 parser.add_option(
1775 "-c",
1776 "--sort-by-count",
1777 action="store_true",
1778 dest="sort_count",
1779 help="display verbose debug info",
1780 default=False,
1782 parser.add_option(
1783 "--crashlog",
1784 type="string",
1785 dest="crashlog",
1786 help="symbolicate using a darwin crash log file",
1787 default=False,
1789 try:
1790 (options, args) = parser.parse_args(sys.argv[1:])
1791 except:
1792 print("error: argument error")
1793 sys.exit(1)
1795 options.colors = TerminalColors(options.color)
1796 options.symbolicator = None
1797 if options.crashlog:
1798 import lldb
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.
1808 if len(args):
1809 for file in args:
1810 print(
1811 "#----------------------------------------------------------------------"
1813 print("# GDB remote log file: '%s'" % file)
1814 print(
1815 "#----------------------------------------------------------------------"
1817 parse_gdb_log_file(file, options)
1818 if options.symbolicator:
1819 print("%s" % (options.symbolicator))
1820 else:
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"
1833 print(
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'