[docs] Update HowToReleaseLLVM documentation.
[llvm-project.git] / lldb / examples / python / gdbremote.py
blob8d000c4792b4374de197aada6091bfd88ca491da
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.'''
103 if self.enabled:
104 if fg:
105 return "\x1b[30m"
106 else:
107 return "\x1b[40m"
108 return ''
110 def red(self, fg=True):
111 '''Set the foreground or background color to red.
112 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
113 if self.enabled:
114 if fg:
115 return "\x1b[31m"
116 else:
117 return "\x1b[41m"
118 return ''
120 def green(self, fg=True):
121 '''Set the foreground or background color to green.
122 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
123 if self.enabled:
124 if fg:
125 return "\x1b[32m"
126 else:
127 return "\x1b[42m"
128 return ''
130 def yellow(self, fg=True):
131 '''Set the foreground or background color to yellow.
132 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
133 if self.enabled:
134 if fg:
135 return "\x1b[33m"
136 else:
137 return "\x1b[43m"
138 return ''
140 def blue(self, fg=True):
141 '''Set the foreground or background color to blue.
142 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
143 if self.enabled:
144 if fg:
145 return "\x1b[34m"
146 else:
147 return "\x1b[44m"
148 return ''
150 def magenta(self, fg=True):
151 '''Set the foreground or background color to magenta.
152 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
153 if self.enabled:
154 if fg:
155 return "\x1b[35m"
156 else:
157 return "\x1b[45m"
158 return ''
160 def cyan(self, fg=True):
161 '''Set the foreground or background color to cyan.
162 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
163 if self.enabled:
164 if fg:
165 return "\x1b[36m"
166 else:
167 return "\x1b[46m"
168 return ''
170 def white(self, fg=True):
171 '''Set the foreground or background color to white.
172 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
173 if self.enabled:
174 if fg:
175 return "\x1b[37m"
176 else:
177 return "\x1b[47m"
178 return ''
180 def default(self, fg=True):
181 '''Set the foreground or background color to the default.
182 The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.'''
183 if self.enabled:
184 if fg:
185 return "\x1b[39m"
186 else:
187 return "\x1b[49m"
188 return ''
191 def start_gdb_log(debugger, command, result, dict):
192 '''Start logging GDB remote packets by enabling logging with timestamps and
193 thread safe logging. Follow a call to this function with a call to "stop_gdb_log"
194 in order to dump out the commands.'''
195 global g_log_file
196 command_args = shlex.split(command)
197 usage = "usage: start_gdb_log [options] [<LOGFILEPATH>]"
198 description = '''The command enables GDB remote packet logging with timestamps. The packets will be logged to <LOGFILEPATH> if supplied, or a temporary file will be used. Logging stops when stop_gdb_log is called and the packet times will
199 be aggregated and displayed.'''
200 parser = optparse.OptionParser(
201 description=description,
202 prog='start_gdb_log',
203 usage=usage)
204 parser.add_option(
205 '-v',
206 '--verbose',
207 action='store_true',
208 dest='verbose',
209 help='display verbose debug info',
210 default=False)
211 try:
212 (options, args) = parser.parse_args(command_args)
213 except:
214 return
216 if g_log_file:
217 result.PutCString(
218 'error: logging is already in progress with file "%s"' %
219 g_log_file)
220 else:
221 args_len = len(args)
222 if args_len == 0:
223 g_log_file = tempfile.mktemp()
224 elif len(args) == 1:
225 g_log_file = args[0]
227 if g_log_file:
228 debugger.HandleCommand(
229 'log enable --threadsafe --timestamp --file "%s" gdb-remote packets' %
230 g_log_file)
231 result.PutCString(
232 "GDB packet logging enable with log file '%s'\nUse the 'stop_gdb_log' command to stop logging and show packet statistics." %
233 g_log_file)
234 return
236 result.PutCString('error: invalid log file path')
237 result.PutCString(usage)
240 def stop_gdb_log(debugger, command, result, dict):
241 '''Stop logging GDB remote packets to the file that was specified in a call
242 to "start_gdb_log" and normalize the timestamps to be relative to the first
243 timestamp in the log file. Also print out statistics for how long each
244 command took to allow performance bottlenecks to be determined.'''
245 global g_log_file
246 # Any commands whose names might be followed by more valid C identifier
247 # characters must be listed here
248 command_args = shlex.split(command)
249 usage = "usage: stop_gdb_log [options]"
250 description = '''The command stops a previously enabled GDB remote packet logging command. Packet logging must have been previously enabled with a call to start_gdb_log.'''
251 parser = optparse.OptionParser(
252 description=description,
253 prog='stop_gdb_log',
254 usage=usage)
255 parser.add_option(
256 '-v',
257 '--verbose',
258 action='store_true',
259 dest='verbose',
260 help='display verbose debug info',
261 default=False)
262 parser.add_option(
263 '--plot',
264 action='store_true',
265 dest='plot',
266 help='plot packet latencies by packet type',
267 default=False)
268 parser.add_option(
269 '-q',
270 '--quiet',
271 action='store_true',
272 dest='quiet',
273 help='display verbose debug info',
274 default=False)
275 parser.add_option(
276 '-C',
277 '--color',
278 action='store_true',
279 dest='color',
280 help='add terminal colors',
281 default=False)
282 parser.add_option(
283 '-c',
284 '--sort-by-count',
285 action='store_true',
286 dest='sort_count',
287 help='display verbose debug info',
288 default=False)
289 parser.add_option(
290 '-s',
291 '--symbolicate',
292 action='store_true',
293 dest='symbolicate',
294 help='symbolicate addresses in log using current "lldb.target"',
295 default=False)
296 try:
297 (options, args) = parser.parse_args(command_args)
298 except:
299 return
300 options.colors = TerminalColors(options.color)
301 options.symbolicator = None
302 if options.symbolicate:
303 if lldb.target:
304 import lldb.utils.symbolication
305 options.symbolicator = lldb.utils.symbolication.Symbolicator()
306 options.symbolicator.target = lldb.target
307 else:
308 print("error: can't symbolicate without a target")
310 if not g_log_file:
311 result.PutCString(
312 'error: logging must have been previously enabled with a call to "stop_gdb_log"')
313 elif os.path.exists(g_log_file):
314 if len(args) == 0:
315 debugger.HandleCommand('log disable gdb-remote packets')
316 result.PutCString(
317 "GDB packet logging disabled. Logged packets are in '%s'" %
318 g_log_file)
319 parse_gdb_log_file(g_log_file, options)
320 else:
321 result.PutCString(usage)
322 else:
323 print('error: the GDB packet log file "%s" does not exist' % g_log_file)
326 def is_hex_byte(str):
327 if len(str) == 2:
328 return str[0] in string.hexdigits and str[1] in string.hexdigits
329 return False
331 def get_hex_string_if_all_printable(str):
332 try:
333 s = binascii.unhexlify(str).decode()
334 if all(c in string.printable for c in s):
335 return s
336 except (TypeError, binascii.Error, UnicodeDecodeError):
337 pass
338 return None
340 # global register info list
341 g_register_infos = list()
342 g_max_register_info_name_len = 0
345 class RegisterInfo:
346 """Class that represents register information"""
348 def __init__(self, kvp):
349 self.info = dict()
350 for kv in kvp:
351 key = kv[0]
352 value = kv[1]
353 self.info[key] = value
355 def name(self):
356 '''Get the name of the register.'''
357 if self.info and 'name' in self.info:
358 return self.info['name']
359 return None
361 def bit_size(self):
362 '''Get the size in bits of the register.'''
363 if self.info and 'bitsize' in self.info:
364 return int(self.info['bitsize'])
365 return 0
367 def byte_size(self):
368 '''Get the size in bytes of the register.'''
369 return self.bit_size() / 8
371 def get_value_from_hex_string(self, hex_str):
372 '''Dump the register value given a native byte order encoded hex ASCII byte string.'''
373 encoding = self.info['encoding']
374 bit_size = self.bit_size()
375 packet = Packet(hex_str)
376 if encoding == 'uint':
377 uval = packet.get_hex_uint(g_byte_order)
378 if bit_size == 8:
379 return '0x%2.2x' % (uval)
380 elif bit_size == 16:
381 return '0x%4.4x' % (uval)
382 elif bit_size == 32:
383 return '0x%8.8x' % (uval)
384 elif bit_size == 64:
385 return '0x%16.16x' % (uval)
386 bytes = list()
387 uval = packet.get_hex_uint8()
388 while uval is not None:
389 bytes.append(uval)
390 uval = packet.get_hex_uint8()
391 value_str = '0x'
392 if g_byte_order == 'little':
393 bytes.reverse()
394 for byte in bytes:
395 value_str += '%2.2x' % byte
396 return '%s' % (value_str)
398 def __str__(self):
399 '''Dump the register info key/value pairs'''
400 s = ''
401 for key in self.info.keys():
402 if s:
403 s += ', '
404 s += "%s=%s " % (key, self.info[key])
405 return s
408 class Packet:
409 """Class that represents a packet that contains string data"""
411 def __init__(self, packet_str):
412 self.str = packet_str
414 def peek_char(self):
415 ch = 0
416 if self.str:
417 ch = self.str[0]
418 return ch
420 def get_char(self):
421 ch = 0
422 if self.str:
423 ch = self.str[0]
424 self.str = self.str[1:]
425 return ch
427 def skip_exact_string(self, s):
428 if self.str and self.str.startswith(s):
429 self.str = self.str[len(s):]
430 return True
431 else:
432 return False
434 def get_thread_id(self, fail_value=-1):
435 match = g_number_regex.match(self.str)
436 if match:
437 number_str = match.group(1)
438 self.str = self.str[len(number_str):]
439 return int(number_str, 0)
440 else:
441 return fail_value
443 def get_hex_uint8(self):
444 if self.str and len(self.str) >= 2 and self.str[
445 0] in string.hexdigits and self.str[1] in string.hexdigits:
446 uval = int(self.str[0:2], 16)
447 self.str = self.str[2:]
448 return uval
449 return None
451 def get_hex_uint16(self, byte_order):
452 uval = 0
453 if byte_order == 'big':
454 uval |= self.get_hex_uint8() << 8
455 uval |= self.get_hex_uint8()
456 else:
457 uval |= self.get_hex_uint8()
458 uval |= self.get_hex_uint8() << 8
459 return uval
461 def get_hex_uint32(self, byte_order):
462 uval = 0
463 if byte_order == 'big':
464 uval |= self.get_hex_uint8() << 24
465 uval |= self.get_hex_uint8() << 16
466 uval |= self.get_hex_uint8() << 8
467 uval |= self.get_hex_uint8()
468 else:
469 uval |= self.get_hex_uint8()
470 uval |= self.get_hex_uint8() << 8
471 uval |= self.get_hex_uint8() << 16
472 uval |= self.get_hex_uint8() << 24
473 return uval
475 def get_hex_uint64(self, byte_order):
476 uval = 0
477 if byte_order == 'big':
478 uval |= self.get_hex_uint8() << 56
479 uval |= self.get_hex_uint8() << 48
480 uval |= self.get_hex_uint8() << 40
481 uval |= self.get_hex_uint8() << 32
482 uval |= self.get_hex_uint8() << 24
483 uval |= self.get_hex_uint8() << 16
484 uval |= self.get_hex_uint8() << 8
485 uval |= self.get_hex_uint8()
486 else:
487 uval |= self.get_hex_uint8()
488 uval |= self.get_hex_uint8() << 8
489 uval |= self.get_hex_uint8() << 16
490 uval |= self.get_hex_uint8() << 24
491 uval |= self.get_hex_uint8() << 32
492 uval |= self.get_hex_uint8() << 40
493 uval |= self.get_hex_uint8() << 48
494 uval |= self.get_hex_uint8() << 56
495 return uval
497 def get_number(self, fail_value=-1):
498 '''Get a number from the packet. The number must be in big endian format and should be parsed
499 according to its prefix (starts with "0x" means hex, starts with "0" means octal, starts with
500 [1-9] means decimal, etc)'''
501 match = g_number_regex.match(self.str)
502 if match:
503 number_str = match.group(1)
504 self.str = self.str[len(number_str):]
505 return int(number_str, 0)
506 else:
507 return fail_value
509 def get_hex_ascii_str(self, n=0):
510 hex_chars = self.get_hex_chars(n)
511 if hex_chars:
512 return binascii.unhexlify(hex_chars)
513 else:
514 return None
516 def get_hex_chars(self, n=0):
517 str_len = len(self.str)
518 if n == 0:
519 # n was zero, so we need to determine all hex chars and
520 # stop when we hit the end of the string of a non-hex character
521 while n < str_len and self.str[n] in string.hexdigits:
522 n = n + 1
523 else:
524 if n > str_len:
525 return None # Not enough chars
526 # Verify all chars are hex if a length was specified
527 for i in range(n):
528 if self.str[i] not in string.hexdigits:
529 return None # Not all hex digits
530 if n == 0:
531 return None
532 hex_str = self.str[0:n]
533 self.str = self.str[n:]
534 return hex_str
536 def get_hex_uint(self, byte_order, n=0):
537 if byte_order == 'big':
538 hex_str = self.get_hex_chars(n)
539 if hex_str is None:
540 return None
541 return int(hex_str, 16)
542 else:
543 uval = self.get_hex_uint8()
544 if uval is None:
545 return None
546 uval_result = 0
547 shift = 0
548 while uval is not None:
549 uval_result |= (uval << shift)
550 shift += 8
551 uval = self.get_hex_uint8()
552 return uval_result
554 def get_key_value_pairs(self):
555 kvp = list()
556 if ';' in self.str:
557 key_value_pairs = self.str.split(';')
558 for key_value_pair in key_value_pairs:
559 if len(key_value_pair):
560 kvp.append(key_value_pair.split(':', 1))
561 return kvp
563 def split(self, ch):
564 return self.str.split(ch)
566 def split_hex(self, ch, byte_order):
567 hex_values = list()
568 strings = self.str.split(ch)
569 for str in strings:
570 hex_values.append(Packet(str).get_hex_uint(byte_order))
571 return hex_values
573 def __str__(self):
574 return self.str
576 def __len__(self):
577 return len(self.str)
579 g_thread_suffix_regex = re.compile(';thread:([0-9a-fA-F]+);')
582 def get_thread_from_thread_suffix(str):
583 if str:
584 match = g_thread_suffix_regex.match(str)
585 if match:
586 return int(match.group(1), 16)
587 return None
590 def cmd_qThreadStopInfo(options, cmd, args):
591 packet = Packet(args)
592 tid = packet.get_hex_uint('big')
593 print("get_thread_stop_info (tid = 0x%x)" % (tid))
596 def cmd_stop_reply(options, cmd, args):
597 print("get_last_stop_info()")
598 return False
601 def rsp_stop_reply(options, cmd, cmd_args, rsp):
602 global g_byte_order
603 packet = Packet(rsp)
604 stop_type = packet.get_char()
605 if stop_type == 'T' or stop_type == 'S':
606 signo = packet.get_hex_uint8()
607 key_value_pairs = packet.get_key_value_pairs()
608 for key_value_pair in key_value_pairs:
609 key = key_value_pair[0]
610 if is_hex_byte(key):
611 reg_num = Packet(key).get_hex_uint8()
612 if reg_num < len(g_register_infos):
613 reg_info = g_register_infos[reg_num]
614 key_value_pair[0] = reg_info.name()
615 key_value_pair[1] = reg_info.get_value_from_hex_string(
616 key_value_pair[1])
617 elif key == 'jthreads' or key == 'jstopinfo':
618 key_value_pair[1] = binascii.unhexlify(key_value_pair[1])
619 key_value_pairs.insert(0, ['signal', signo])
620 print('stop_reply():')
621 dump_key_value_pairs(key_value_pairs)
622 elif stop_type == 'W':
623 exit_status = packet.get_hex_uint8()
624 print('stop_reply(): exit (status=%i)' % exit_status)
625 elif stop_type == 'O':
626 print('stop_reply(): stdout = "%s"' % packet.str)
629 def cmd_unknown_packet(options, cmd, args):
630 if args:
631 print("cmd: %s, args: %s", cmd, args)
632 else:
633 print("cmd: %s", cmd)
634 return False
637 def cmd_qSymbol(options, cmd, args):
638 if args == ':':
639 print('ready to serve symbols')
640 else:
641 packet = Packet(args)
642 symbol_addr = packet.get_hex_uint('big')
643 if symbol_addr is None:
644 if packet.skip_exact_string(':'):
645 symbol_name = packet.get_hex_ascii_str()
646 print('lookup_symbol("%s") -> symbol not available yet' % (symbol_name))
647 else:
648 print('error: bad command format')
649 else:
650 if packet.skip_exact_string(':'):
651 symbol_name = packet.get_hex_ascii_str()
652 print('lookup_symbol("%s") -> 0x%x' % (symbol_name, symbol_addr))
653 else:
654 print('error: bad command format')
656 def cmd_QSetWithHexString(options, cmd, args):
657 print('%s("%s")' % (cmd[:-1], binascii.unhexlify(args)))
659 def cmd_QSetWithString(options, cmd, args):
660 print('%s("%s")' % (cmd[:-1], args))
662 def cmd_QSetWithUnsigned(options, cmd, args):
663 print('%s(%i)' % (cmd[:-1], int(args)))
665 def rsp_qSymbol(options, cmd, cmd_args, rsp):
666 if len(rsp) == 0:
667 print("Unsupported")
668 else:
669 if rsp == "OK":
670 print("No more symbols to lookup")
671 else:
672 packet = Packet(rsp)
673 if packet.skip_exact_string("qSymbol:"):
674 symbol_name = packet.get_hex_ascii_str()
675 print('lookup_symbol("%s")' % (symbol_name))
676 else:
677 print('error: response string should start with "qSymbol:": respnse is "%s"' % (rsp))
680 def cmd_qXfer(options, cmd, args):
681 # $qXfer:features:read:target.xml:0,1ffff#14
682 print("read target special data %s" % (args))
683 return True
686 def rsp_qXfer(options, cmd, cmd_args, rsp):
687 data = cmd_args.split(':')
688 if data[0] == 'features':
689 if data[1] == 'read':
690 filename, extension = os.path.splitext(data[2])
691 if extension == '.xml':
692 response = Packet(rsp)
693 xml_string = response.get_hex_ascii_str()
694 if xml_string:
695 ch = xml_string[0]
696 if ch == 'l':
697 xml_string = xml_string[1:]
698 xml_root = ET.fromstring(xml_string)
699 for reg_element in xml_root.findall("./feature/reg"):
700 if not 'value_regnums' in reg_element.attrib:
701 reg_info = RegisterInfo([])
702 if 'name' in reg_element.attrib:
703 reg_info.info[
704 'name'] = reg_element.attrib['name']
705 else:
706 reg_info.info['name'] = 'unspecified'
707 if 'encoding' in reg_element.attrib:
708 reg_info.info['encoding'] = reg_element.attrib[
709 'encoding']
710 else:
711 reg_info.info['encoding'] = 'uint'
712 if 'offset' in reg_element.attrib:
713 reg_info.info[
714 'offset'] = reg_element.attrib['offset']
715 if 'bitsize' in reg_element.attrib:
716 reg_info.info[
717 'bitsize'] = reg_element.attrib['bitsize']
718 g_register_infos.append(reg_info)
719 print('XML for "%s":' % (data[2]))
720 ET.dump(xml_root)
723 def cmd_A(options, cmd, args):
724 print('launch process:')
725 packet = Packet(args)
726 while True:
727 arg_len = packet.get_number()
728 if arg_len == -1:
729 break
730 if not packet.skip_exact_string(','):
731 break
732 arg_idx = packet.get_number()
733 if arg_idx == -1:
734 break
735 if not packet.skip_exact_string(','):
736 break
737 arg_value = packet.get_hex_ascii_str(arg_len)
738 print('argv[%u] = "%s"' % (arg_idx, arg_value))
741 def cmd_qC(options, cmd, args):
742 print("query_current_thread_id()")
745 def rsp_qC(options, cmd, cmd_args, rsp):
746 packet = Packet(rsp)
747 if packet.skip_exact_string("QC"):
748 tid = packet.get_thread_id()
749 print("current_thread_id = %#x" % (tid))
750 else:
751 print("current_thread_id = old thread ID")
754 def cmd_query_packet(options, cmd, args):
755 if args:
756 print("%s%s" % (cmd, args))
757 else:
758 print("%s" % (cmd))
759 return False
762 def rsp_ok_error(rsp):
763 print("rsp: ", rsp)
766 def rsp_ok_means_supported(options, cmd, cmd_args, rsp):
767 if rsp == 'OK':
768 print("%s%s is supported" % (cmd, cmd_args))
769 elif rsp == '':
770 print("%s%s is not supported" % (cmd, cmd_args))
771 else:
772 print("%s%s -> %s" % (cmd, cmd_args, rsp))
775 def rsp_ok_means_success(options, cmd, cmd_args, rsp):
776 if rsp == 'OK':
777 print("success")
778 elif rsp == '':
779 print("%s%s is not supported" % (cmd, cmd_args))
780 else:
781 print("%s%s -> %s" % (cmd, cmd_args, rsp))
784 def dump_key_value_pairs(key_value_pairs):
785 max_key_len = 0
786 for key_value_pair in key_value_pairs:
787 key_len = len(key_value_pair[0])
788 if max_key_len < key_len:
789 max_key_len = key_len
790 for key_value_pair in key_value_pairs:
791 key = key_value_pair[0]
792 value = key_value_pair[1]
793 unhex_value = get_hex_string_if_all_printable(value)
794 if unhex_value:
795 print("%*s = %s (%s)" % (max_key_len, key, value, unhex_value))
796 else:
797 print("%*s = %s" % (max_key_len, key, value))
800 def rsp_dump_key_value_pairs(options, cmd, cmd_args, rsp):
801 if rsp:
802 print('%s response:' % (cmd))
803 packet = Packet(rsp)
804 key_value_pairs = packet.get_key_value_pairs()
805 dump_key_value_pairs(key_value_pairs)
806 else:
807 print("not supported")
810 def cmd_c(options, cmd, args):
811 print("continue()")
812 return False
815 def cmd_s(options, cmd, args):
816 print("step()")
817 return False
820 def cmd_qSpeedTest(options, cmd, args):
821 print(("qSpeedTest: cmd='%s', args='%s'" % (cmd, args)))
824 def rsp_qSpeedTest(options, cmd, cmd_args, rsp):
825 print(("qSpeedTest: rsp='%s' cmd='%s', args='%s'" % (rsp, cmd, args)))
828 def cmd_vCont(options, cmd, args):
829 if args == '?':
830 print("%s: get supported extended continue modes" % (cmd))
831 else:
832 got_other_threads = 0
833 s = ''
834 for thread_action in args[1:].split(';'):
835 (short_action, thread) = thread_action.split(':', 1)
836 tid = int(thread, 16)
837 if short_action == 'c':
838 action = 'continue'
839 elif short_action == 's':
840 action = 'step'
841 elif short_action[0] == 'C':
842 action = 'continue with signal 0x%s' % (short_action[1:])
843 elif short_action == 'S':
844 action = 'step with signal 0x%s' % (short_action[1:])
845 else:
846 action = short_action
847 if s:
848 s += ', '
849 if tid == -1:
850 got_other_threads = 1
851 s += 'other-threads:'
852 else:
853 s += 'thread 0x%4.4x: %s' % (tid, action)
854 if got_other_threads:
855 print("extended_continue (%s)" % (s))
856 else:
857 print("extended_continue (%s, other-threads: suspend)" % (s))
858 return False
861 def rsp_vCont(options, cmd, cmd_args, rsp):
862 if cmd_args == '?':
863 # Skip the leading 'vCont;'
864 rsp = rsp[6:]
865 modes = rsp.split(';')
866 s = "%s: supported extended continue modes include: " % (cmd)
868 for i, mode in enumerate(modes):
869 if i:
870 s += ', '
871 if mode == 'c':
872 s += 'continue'
873 elif mode == 'C':
874 s += 'continue with signal'
875 elif mode == 's':
876 s += 'step'
877 elif mode == 'S':
878 s += 'step with signal'
879 elif mode == 't':
880 s += 'stop'
881 # else:
882 # s += 'unrecognized vCont mode: ', str(mode)
883 print(s)
884 elif rsp:
885 if rsp[0] == 'T' or rsp[0] == 'S' or rsp[0] == 'W' or rsp[0] == 'X':
886 rsp_stop_reply(options, cmd, cmd_args, rsp)
887 return
888 if rsp[0] == 'O':
889 print("stdout: %s" % (rsp))
890 return
891 else:
892 print("not supported (cmd = '%s', args = '%s', rsp = '%s')" % (cmd, cmd_args, rsp))
895 def cmd_vAttach(options, cmd, args):
896 (extra_command, args) = args.split(';')
897 if extra_command:
898 print("%s%s(%s)" % (cmd, extra_command, args))
899 else:
900 print("attach(pid = %u)" % int(args, 16))
901 return False
904 def cmd_qRegisterInfo(options, cmd, args):
905 print('query_register_info(reg_num=%i)' % (int(args, 16)))
906 return False
909 def rsp_qRegisterInfo(options, cmd, cmd_args, rsp):
910 global g_max_register_info_name_len
911 print('query_register_info(reg_num=%i):' % (int(cmd_args, 16)), end=' ')
912 if len(rsp) == 3 and rsp[0] == 'E':
913 g_max_register_info_name_len = 0
914 for reg_info in g_register_infos:
915 name_len = len(reg_info.name())
916 if g_max_register_info_name_len < name_len:
917 g_max_register_info_name_len = name_len
918 print(' DONE')
919 else:
920 packet = Packet(rsp)
921 reg_info = RegisterInfo(packet.get_key_value_pairs())
922 g_register_infos.append(reg_info)
923 print(reg_info)
924 return False
927 def cmd_qThreadInfo(options, cmd, args):
928 if cmd == 'qfThreadInfo':
929 query_type = 'first'
930 else:
931 query_type = 'subsequent'
932 print('get_current_thread_list(type=%s)' % (query_type))
933 return False
936 def rsp_qThreadInfo(options, cmd, cmd_args, rsp):
937 packet = Packet(rsp)
938 response_type = packet.get_char()
939 if response_type == 'm':
940 tids = packet.split_hex(';', 'big')
941 for i, tid in enumerate(tids):
942 if i:
943 print(',', end=' ')
944 print('0x%x' % (tid), end=' ')
945 print()
946 elif response_type == 'l':
947 print('END')
950 def rsp_hex_big_endian(options, cmd, cmd_args, rsp):
951 if rsp == '':
952 print("%s%s is not supported" % (cmd, cmd_args))
953 else:
954 packet = Packet(rsp)
955 uval = packet.get_hex_uint('big')
956 print('%s: 0x%x' % (cmd, uval))
959 def cmd_read_mem_bin(options, cmd, args):
960 # x0x7fff5fc39200,0x200
961 packet = Packet(args)
962 addr = packet.get_hex_uint('big')
963 comma = packet.get_char()
964 size = packet.get_hex_uint('big')
965 print('binary_read_memory (addr = 0x%16.16x, size = %u)' % (addr, size))
966 return False
969 def rsp_mem_bin_bytes(options, cmd, cmd_args, rsp):
970 packet = Packet(cmd_args)
971 addr = packet.get_hex_uint('big')
972 comma = packet.get_char()
973 size = packet.get_hex_uint('big')
974 print('memory:')
975 if size > 0:
976 dump_hex_memory_buffer(addr, rsp)
979 def cmd_read_memory(options, cmd, args):
980 packet = Packet(args)
981 addr = packet.get_hex_uint('big')
982 comma = packet.get_char()
983 size = packet.get_hex_uint('big')
984 print('read_memory (addr = 0x%16.16x, size = %u)' % (addr, size))
985 return False
988 def dump_hex_memory_buffer(addr, hex_byte_str):
989 packet = Packet(hex_byte_str)
990 idx = 0
991 ascii = ''
992 uval = packet.get_hex_uint8()
993 while uval is not None:
994 if ((idx % 16) == 0):
995 if ascii:
996 print(' ', ascii)
997 ascii = ''
998 print('0x%x:' % (addr + idx), end=' ')
999 print('%2.2x' % (uval), end=' ')
1000 if 0x20 <= uval and uval < 0x7f:
1001 ascii += '%c' % uval
1002 else:
1003 ascii += '.'
1004 uval = packet.get_hex_uint8()
1005 idx = idx + 1
1006 if ascii:
1007 print(' ', ascii)
1008 ascii = ''
1011 def cmd_write_memory(options, cmd, args):
1012 packet = Packet(args)
1013 addr = packet.get_hex_uint('big')
1014 if packet.get_char() != ',':
1015 print('error: invalid write memory command (missing comma after address)')
1016 return
1017 size = packet.get_hex_uint('big')
1018 if packet.get_char() != ':':
1019 print('error: invalid write memory command (missing colon after size)')
1020 return
1021 print('write_memory (addr = 0x%16.16x, size = %u, data:' % (addr, size))
1022 dump_hex_memory_buffer(addr, packet.str)
1023 return False
1026 def cmd_alloc_memory(options, cmd, args):
1027 packet = Packet(args)
1028 byte_size = packet.get_hex_uint('big')
1029 if packet.get_char() != ',':
1030 print('error: invalid allocate memory command (missing comma after address)')
1031 return
1032 print('allocate_memory (byte-size = %u (0x%x), permissions = %s)' % (byte_size, byte_size, packet.str))
1033 return False
1036 def rsp_alloc_memory(options, cmd, cmd_args, rsp):
1037 packet = Packet(rsp)
1038 addr = packet.get_hex_uint('big')
1039 print('addr = 0x%x' % addr)
1042 def cmd_dealloc_memory(options, cmd, args):
1043 packet = Packet(args)
1044 addr = packet.get_hex_uint('big')
1045 if packet.get_char() != ',':
1046 print('error: invalid allocate memory command (missing comma after address)')
1047 else:
1048 print('deallocate_memory (addr = 0x%x, permissions = %s)' % (addr, packet.str))
1049 return False
1052 def rsp_memory_bytes(options, cmd, cmd_args, rsp):
1053 addr = Packet(cmd_args).get_hex_uint('big')
1054 dump_hex_memory_buffer(addr, rsp)
1057 def get_register_name_equal_value(options, reg_num, hex_value_str):
1058 if reg_num < len(g_register_infos):
1059 reg_info = g_register_infos[reg_num]
1060 value_str = reg_info.get_value_from_hex_string(hex_value_str)
1061 s = reg_info.name() + ' = '
1062 if options.symbolicator:
1063 symbolicated_addresses = options.symbolicator.symbolicate(
1064 int(value_str, 0))
1065 if symbolicated_addresses:
1066 s += options.colors.magenta()
1067 s += '%s' % symbolicated_addresses[0]
1068 s += options.colors.reset()
1069 return s
1070 s += value_str
1071 return s
1072 else:
1073 reg_value = Packet(hex_value_str).get_hex_uint(g_byte_order)
1074 return 'reg(%u) = 0x%x' % (reg_num, reg_value)
1077 def cmd_read_one_reg(options, cmd, args):
1078 packet = Packet(args)
1079 reg_num = packet.get_hex_uint('big')
1080 tid = get_thread_from_thread_suffix(packet.str)
1081 name = None
1082 if reg_num < len(g_register_infos):
1083 name = g_register_infos[reg_num].name()
1084 if packet.str:
1085 packet.get_char() # skip ;
1086 thread_info = packet.get_key_value_pairs()
1087 tid = int(thread_info[0][1], 16)
1088 s = 'read_register (reg_num=%u' % reg_num
1089 if name:
1090 s += ' (%s)' % (name)
1091 if tid is not None:
1092 s += ', tid = 0x%4.4x' % (tid)
1093 s += ')'
1094 print(s)
1095 return False
1098 def rsp_read_one_reg(options, cmd, cmd_args, rsp):
1099 packet = Packet(cmd_args)
1100 reg_num = packet.get_hex_uint('big')
1101 print(get_register_name_equal_value(options, reg_num, rsp))
1104 def cmd_write_one_reg(options, cmd, args):
1105 packet = Packet(args)
1106 reg_num = packet.get_hex_uint('big')
1107 if packet.get_char() != '=':
1108 print('error: invalid register write packet')
1109 else:
1110 name = None
1111 hex_value_str = packet.get_hex_chars()
1112 tid = get_thread_from_thread_suffix(packet.str)
1113 s = 'write_register (reg_num=%u' % reg_num
1114 if name:
1115 s += ' (%s)' % (name)
1116 s += ', value = '
1117 s += get_register_name_equal_value(options, reg_num, hex_value_str)
1118 if tid is not None:
1119 s += ', tid = 0x%4.4x' % (tid)
1120 s += ')'
1121 print(s)
1122 return False
1125 def dump_all_regs(packet):
1126 for reg_info in g_register_infos:
1127 nibble_size = reg_info.bit_size() / 4
1128 hex_value_str = packet.get_hex_chars(nibble_size)
1129 if hex_value_str is not None:
1130 value = reg_info.get_value_from_hex_string(hex_value_str)
1131 print('%*s = %s' % (g_max_register_info_name_len, reg_info.name(), value))
1132 else:
1133 return
1136 def cmd_read_all_regs(cmd, cmd_args):
1137 packet = Packet(cmd_args)
1138 packet.get_char() # toss the 'g' command character
1139 tid = get_thread_from_thread_suffix(packet.str)
1140 if tid is not None:
1141 print('read_all_register(thread = 0x%4.4x)' % tid)
1142 else:
1143 print('read_all_register()')
1144 return False
1147 def rsp_read_all_regs(options, cmd, cmd_args, rsp):
1148 packet = Packet(rsp)
1149 dump_all_regs(packet)
1152 def cmd_write_all_regs(options, cmd, args):
1153 packet = Packet(args)
1154 print('write_all_registers()')
1155 dump_all_regs(packet)
1156 return False
1158 g_bp_types = ["software_bp", "hardware_bp", "write_wp", "read_wp", "access_wp"]
1161 def cmd_bp(options, cmd, args):
1162 if cmd == 'Z':
1163 s = 'set_'
1164 else:
1165 s = 'clear_'
1166 packet = Packet(args)
1167 bp_type = packet.get_hex_uint('big')
1168 packet.get_char() # Skip ,
1169 bp_addr = packet.get_hex_uint('big')
1170 packet.get_char() # Skip ,
1171 bp_size = packet.get_hex_uint('big')
1172 s += g_bp_types[bp_type]
1173 s += " (addr = 0x%x, size = %u)" % (bp_addr, bp_size)
1174 print(s)
1175 return False
1178 def cmd_mem_rgn_info(options, cmd, args):
1179 packet = Packet(args)
1180 packet.get_char() # skip ':' character
1181 addr = packet.get_hex_uint('big')
1182 print('get_memory_region_info (addr=0x%x)' % (addr))
1183 return False
1186 def cmd_kill(options, cmd, args):
1187 print('kill_process()')
1188 return False
1191 def cmd_jThreadsInfo(options, cmd, args):
1192 print('jThreadsInfo()')
1193 return False
1196 def cmd_jGetLoadedDynamicLibrariesInfos(options, cmd, args):
1197 print('jGetLoadedDynamicLibrariesInfos()')
1198 return False
1201 def decode_packet(s, start_index=0):
1202 # print '\ndecode_packet("%s")' % (s[start_index:])
1203 index = s.find('}', start_index)
1204 have_escapes = index != -1
1205 if have_escapes:
1206 normal_s = s[start_index:index]
1207 else:
1208 normal_s = s[start_index:]
1209 # print 'normal_s = "%s"' % (normal_s)
1210 if have_escapes:
1211 escape_char = '%c' % (ord(s[index + 1]) ^ 0x20)
1212 # print 'escape_char for "%s" = %c' % (s[index:index+2], escape_char)
1213 return normal_s + escape_char + decode_packet(s, index + 2)
1214 else:
1215 return normal_s
1218 def rsp_json(options, cmd, cmd_args, rsp):
1219 print('%s() reply:' % (cmd))
1220 if not rsp:
1221 return
1222 try:
1223 json_tree = json.loads(rsp)
1224 print(json.dumps(json_tree, indent=4, separators=(',', ': ')))
1225 except json.JSONDecodeError:
1226 return
1228 def rsp_jGetLoadedDynamicLibrariesInfos(options, cmd, cmd_args, rsp):
1229 if cmd_args:
1230 rsp_json(options, cmd, cmd_args, rsp)
1231 else:
1232 rsp_ok_means_supported(options, cmd, cmd_args, rsp)
1234 gdb_remote_commands = {
1235 '\\?': {'cmd': cmd_stop_reply, 'rsp': rsp_stop_reply, 'name': "stop reply pacpket"},
1236 'qThreadStopInfo': {'cmd': cmd_qThreadStopInfo, 'rsp': rsp_stop_reply, 'name': "stop reply pacpket"},
1237 'QStartNoAckMode': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query if no ack mode is supported"},
1238 'QThreadSuffixSupported': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query if thread suffix is supported"},
1239 'QListThreadsInStopReply': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query if threads in stop reply packets are supported"},
1240 'QSetDetachOnError:': {'cmd': cmd_QSetWithUnsigned, 'rsp': rsp_ok_means_success, 'name': "set if we should detach on error"},
1241 'QSetDisableASLR:': {'cmd': cmd_QSetWithUnsigned, 'rsp': rsp_ok_means_success, 'name': "set if we should disable ASLR"},
1242 'qLaunchSuccess': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_success, 'name': "check on launch success for the A packet"},
1243 'A': {'cmd': cmd_A, 'rsp': rsp_ok_means_success, 'name': "launch process"},
1244 'QLaunchArch:': {'cmd': cmd_QSetWithString, 'rsp': rsp_ok_means_supported, 'name': "set the arch to launch in case the file contains multiple architectures"},
1245 'qVAttachOrWaitSupported': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "set the launch architecture"},
1246 'qHostInfo': {'cmd': cmd_query_packet, 'rsp': rsp_dump_key_value_pairs, 'name': "get host information"},
1247 'qC': {'cmd': cmd_qC, 'rsp': rsp_qC, 'name': "return the current thread ID"},
1248 'vCont': {'cmd': cmd_vCont, 'rsp': rsp_vCont, 'name': "extended continue command"},
1249 'qSpeedTest': {'cmd':cmd_qSpeedTest, 'rsp': rsp_qSpeedTest, 'name': 'speed test packdet'},
1250 'vAttach': {'cmd': cmd_vAttach, 'rsp': rsp_stop_reply, 'name': "attach to process"},
1251 'c': {'cmd': cmd_c, 'rsp': rsp_stop_reply, 'name': "continue"},
1252 's': {'cmd': cmd_s, 'rsp': rsp_stop_reply, 'name': "step"},
1253 'qRegisterInfo': {'cmd': cmd_qRegisterInfo, 'rsp': rsp_qRegisterInfo, 'name': "query register info"},
1254 'qfThreadInfo': {'cmd': cmd_qThreadInfo, 'rsp': rsp_qThreadInfo, 'name': "get current thread list"},
1255 'qsThreadInfo': {'cmd': cmd_qThreadInfo, 'rsp': rsp_qThreadInfo, 'name': "get current thread list"},
1256 'qShlibInfoAddr': {'cmd': cmd_query_packet, 'rsp': rsp_hex_big_endian, 'name': "get shared library info address"},
1257 'qMemoryRegionInfo': {'cmd': cmd_mem_rgn_info, 'rsp': rsp_dump_key_value_pairs, 'name': "get memory region information"},
1258 'qProcessInfo': {'cmd': cmd_query_packet, 'rsp': rsp_dump_key_value_pairs, 'name': "get process info"},
1259 'qSupported': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query supported"},
1260 'qXfer:': {'cmd': cmd_qXfer, 'rsp': rsp_qXfer, 'name': "qXfer"},
1261 'qSymbol:': {'cmd': cmd_qSymbol, 'rsp': rsp_qSymbol, 'name': "qSymbol"},
1262 'QSetSTDIN:' : {'cmd' : cmd_QSetWithHexString, 'rsp' : rsp_ok_means_success, 'name': "set STDIN prior to launching with A packet"},
1263 'QSetSTDOUT:' : {'cmd' : cmd_QSetWithHexString, 'rsp' : rsp_ok_means_success, 'name': "set STDOUT prior to launching with A packet"},
1264 'QSetSTDERR:' : {'cmd' : cmd_QSetWithHexString, 'rsp' : rsp_ok_means_success, 'name': "set STDERR prior to launching with A packet"},
1265 'QEnvironment:' : {'cmd' : cmd_QSetWithString, 'rsp' : rsp_ok_means_success, 'name': "set an environment variable prior to launching with A packet"},
1266 'QEnvironmentHexEncoded:' : {'cmd' : cmd_QSetWithHexString, 'rsp' : rsp_ok_means_success, 'name': "set an environment variable prior to launching with A packet"},
1267 'x': {'cmd': cmd_read_mem_bin, 'rsp': rsp_mem_bin_bytes, 'name': "read memory binary"},
1268 'X': {'cmd': cmd_write_memory, 'rsp': rsp_ok_means_success, 'name': "write memory binary"},
1269 'm': {'cmd': cmd_read_memory, 'rsp': rsp_memory_bytes, 'name': "read memory"},
1270 'M': {'cmd': cmd_write_memory, 'rsp': rsp_ok_means_success, 'name': "write memory"},
1271 '_M': {'cmd': cmd_alloc_memory, 'rsp': rsp_alloc_memory, 'name': "allocate memory"},
1272 '_m': {'cmd': cmd_dealloc_memory, 'rsp': rsp_ok_means_success, 'name': "deallocate memory"},
1273 'p': {'cmd': cmd_read_one_reg, 'rsp': rsp_read_one_reg, 'name': "read single register"},
1274 'P': {'cmd': cmd_write_one_reg, 'rsp': rsp_ok_means_success, 'name': "write single register"},
1275 'g': {'cmd': cmd_read_all_regs, 'rsp': rsp_read_all_regs, 'name': "read all registers"},
1276 'G': {'cmd': cmd_write_all_regs, 'rsp': rsp_ok_means_success, 'name': "write all registers"},
1277 'z': {'cmd': cmd_bp, 'rsp': rsp_ok_means_success, 'name': "clear breakpoint or watchpoint"},
1278 'Z': {'cmd': cmd_bp, 'rsp': rsp_ok_means_success, 'name': "set breakpoint or watchpoint"},
1279 'k': {'cmd': cmd_kill, 'rsp': rsp_stop_reply, 'name': "kill process"},
1280 'jThreadsInfo': {'cmd': cmd_jThreadsInfo, 'rsp': rsp_json, 'name': "JSON get all threads info"},
1281 'jGetLoadedDynamicLibrariesInfos:': {'cmd': cmd_jGetLoadedDynamicLibrariesInfos, 'rsp': rsp_jGetLoadedDynamicLibrariesInfos, 'name': 'JSON get loaded dynamic libraries'},
1285 def calculate_mean_and_standard_deviation(floats):
1286 sum = 0.0
1287 count = len(floats)
1288 if count == 0:
1289 return (0.0, 0.0)
1290 for f in floats:
1291 sum += f
1292 mean = sum / count
1293 accum = 0.0
1294 for f in floats:
1295 delta = f - mean
1296 accum += delta * delta
1298 std_dev = math.sqrt(accum / (count - 1))
1299 return (mean, std_dev)
1302 def parse_gdb_log_file(path, options):
1303 f = open(path)
1304 parse_gdb_log(f, options)
1305 f.close()
1308 def round_up(n, incr):
1309 return float(((int(n) + incr) / incr) * incr)
1312 def plot_latencies(sec_times):
1313 # import numpy as np
1314 import matplotlib.pyplot as plt
1316 for (i, name) in enumerate(sec_times.keys()):
1317 times = sec_times[name]
1318 if len(times) <= 1:
1319 continue
1320 plt.subplot(2, 1, 1)
1321 plt.title('Packet "%s" Times' % (name))
1322 plt.xlabel('Packet')
1323 units = 'ms'
1324 adj_times = []
1325 max_time = 0.0
1326 for time in times:
1327 time = time * 1000.0
1328 adj_times.append(time)
1329 if time > max_time:
1330 max_time = time
1331 if max_time < 1.0:
1332 units = 'us'
1333 max_time = 0.0
1334 for i in range(len(adj_times)):
1335 adj_times[i] *= 1000.0
1336 if adj_times[i] > max_time:
1337 max_time = adj_times[i]
1338 plt.ylabel('Time (%s)' % (units))
1339 max_y = None
1340 for i in [5.0, 10.0, 25.0, 50.0]:
1341 if max_time < i:
1342 max_y = round_up(max_time, i)
1343 break
1344 if max_y is None:
1345 max_y = round_up(max_time, 100.0)
1346 plt.ylim(0.0, max_y)
1347 plt.plot(adj_times, 'o-')
1348 plt.show()
1351 def parse_gdb_log(file, options):
1352 '''Parse a GDB log file that was generated by enabling logging with:
1353 (lldb) log enable --threadsafe --timestamp --file <FILE> gdb-remote packets
1354 This log file will contain timestamps and this function will then normalize
1355 those packets to be relative to the first value timestamp that is found and
1356 show delta times between log lines and also keep track of how long it takes
1357 for GDB remote commands to make a send/receive round trip. This can be
1358 handy when trying to figure out why some operation in the debugger is taking
1359 a long time during a preset set of debugger commands.'''
1361 tricky_commands = ['qRegisterInfo']
1362 timestamp_regex = re.compile('(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$')
1363 packet_name_regex = re.compile('([A-Za-z_]+)[^a-z]')
1364 packet_transmit_name_regex = re.compile(
1365 '(?P<direction>send|read) packet: (?P<packet>.*)')
1366 packet_contents_name_regex = re.compile('\$([^#]*)#[0-9a-fA-F]{2}')
1367 packet_checksum_regex = re.compile('.*#[0-9a-fA-F]{2}$')
1368 packet_names_regex_str = '(' + \
1369 '|'.join(gdb_remote_commands.keys()) + ')(.*)'
1370 packet_names_regex = re.compile(packet_names_regex_str)
1372 base_time = 0.0
1373 last_time = 0.0
1374 min_time = 100000000.0
1375 packet_total_times = {}
1376 all_packet_times = []
1377 packet_times = {}
1378 packet_counts = {}
1379 lines = file.read().splitlines()
1380 last_command = None
1381 last_command_args = None
1382 last_command_packet = None
1383 hide_next_response = False
1384 num_lines = len(lines)
1385 skip_count = 0
1386 for (line_index, line) in enumerate(lines):
1387 # See if we need to skip any lines
1388 if skip_count > 0:
1389 skip_count -= 1
1390 continue
1391 m = packet_transmit_name_regex.search(line)
1392 is_command = False
1393 direction = None
1394 if m:
1395 direction = m.group('direction')
1396 is_command = direction == 'send'
1397 packet = m.group('packet')
1398 sys.stdout.write(options.colors.green())
1399 if not options.quiet and not hide_next_response:
1400 print('# ', line)
1401 sys.stdout.write(options.colors.reset())
1403 # print 'direction = "%s", packet = "%s"' % (direction, packet)
1405 if packet[0] == '+':
1406 if is_command:
1407 print('-->', end=' ')
1408 else:
1409 print('<--', end=' ')
1410 if not options.quiet:
1411 print('ACK')
1412 continue
1413 elif packet[0] == '-':
1414 if is_command:
1415 print('-->', end=' ')
1416 else:
1417 print('<--', end=' ')
1418 if not options.quiet:
1419 print('NACK')
1420 continue
1421 elif packet[0] == '$':
1422 m = packet_contents_name_regex.match(packet)
1423 if not m and packet[0] == '$':
1424 multiline_packet = packet
1425 idx = line_index + 1
1426 while idx < num_lines:
1427 if not options.quiet and not hide_next_response:
1428 print('# ', lines[idx])
1429 multiline_packet += lines[idx]
1430 m = packet_contents_name_regex.match(multiline_packet)
1431 if m:
1432 packet = multiline_packet
1433 skip_count = idx - line_index
1434 break
1435 else:
1436 idx += 1
1437 if m:
1438 if is_command:
1439 print('-->', end=' ')
1440 else:
1441 print('<--', end=' ')
1442 contents = decode_packet(m.group(1))
1443 if is_command:
1444 hide_next_response = False
1445 m = packet_names_regex.match(contents)
1446 if m:
1447 last_command = m.group(1)
1448 if last_command == '?':
1449 last_command = '\\?'
1450 packet_name = last_command
1451 last_command_args = m.group(2)
1452 last_command_packet = contents
1453 hide_next_response = gdb_remote_commands[last_command][
1454 'cmd'](options, last_command, last_command_args)
1455 else:
1456 packet_match = packet_name_regex.match(contents)
1457 if packet_match:
1458 packet_name = packet_match.group(1)
1459 for tricky_cmd in tricky_commands:
1460 if packet_name.find(tricky_cmd) == 0:
1461 packet_name = tricky_cmd
1462 else:
1463 packet_name = contents
1464 last_command = None
1465 last_command_args = None
1466 last_command_packet = None
1467 elif last_command:
1468 gdb_remote_commands[last_command]['rsp'](
1469 options, last_command, last_command_args, contents)
1470 else:
1471 print('error: invalid packet: "', packet, '"')
1472 else:
1473 print('???')
1474 else:
1475 print('## ', line)
1476 match = timestamp_regex.match(line)
1477 if match:
1478 curr_time = float(match.group(2))
1479 if last_time and not is_command:
1480 delta = curr_time - last_time
1481 all_packet_times.append(delta)
1482 delta = 0.0
1483 if base_time:
1484 delta = curr_time - last_time
1485 else:
1486 base_time = curr_time
1488 if not is_command:
1489 if line.find('read packet: $') >= 0 and packet_name:
1490 if packet_name in packet_total_times:
1491 packet_total_times[packet_name] += delta
1492 packet_counts[packet_name] += 1
1493 else:
1494 packet_total_times[packet_name] = delta
1495 packet_counts[packet_name] = 1
1496 if packet_name not in packet_times:
1497 packet_times[packet_name] = []
1498 packet_times[packet_name].append(delta)
1499 packet_name = None
1500 if min_time > delta:
1501 min_time = delta
1503 if not options or not options.quiet:
1504 print('%s%.6f %+.6f%s' % (match.group(1),
1505 curr_time - base_time,
1506 delta,
1507 match.group(3)))
1508 last_time = curr_time
1509 # else:
1510 # print line
1511 (average, std_dev) = calculate_mean_and_standard_deviation(all_packet_times)
1512 if average and std_dev:
1513 print('%u packets with average packet time of %f and standard deviation of %f' % (len(all_packet_times), average, std_dev))
1514 if packet_total_times:
1515 total_packet_time = 0.0
1516 total_packet_count = 0
1517 for key, vvv in packet_total_times.items():
1518 # print ' key = (%s) "%s"' % (type(key), key)
1519 # print 'value = (%s) %s' % (type(vvv), vvv)
1520 # if type(vvv) == 'float':
1521 total_packet_time += vvv
1522 for key, vvv in packet_counts.items():
1523 total_packet_count += vvv
1525 print('#------------------------------------------------------------')
1526 print('# Packet timing summary:')
1527 print('# Totals: time = %6f, count = %6d' % (total_packet_time,
1528 total_packet_count))
1529 print('# Min packet time: time = %6f' % (min_time))
1530 print('#------------------------------------------------------------')
1531 print('# Packet Time (sec) Percent Count Latency')
1532 print('#------------------------- ----------- ------- ------ -------')
1533 if options and options.sort_count:
1534 res = sorted(
1535 packet_counts,
1536 key=packet_counts.__getitem__,
1537 reverse=True)
1538 else:
1539 res = sorted(
1540 packet_total_times,
1541 key=packet_total_times.__getitem__,
1542 reverse=True)
1544 if last_time > 0.0:
1545 for item in res:
1546 packet_total_time = packet_total_times[item]
1547 packet_percent = (
1548 packet_total_time / total_packet_time) * 100.0
1549 packet_count = packet_counts[item]
1550 print(" %24s %11.6f %5.2f%% %6d %9.6f" % (
1551 item, packet_total_time, packet_percent, packet_count,
1552 float(packet_total_time) / float(packet_count)))
1553 if options and options.plot:
1554 plot_latencies(packet_times)
1556 if __name__ == '__main__':
1557 usage = "usage: gdbremote [options]"
1558 description = '''The command disassembles a GDB remote packet log.'''
1559 parser = optparse.OptionParser(
1560 description=description,
1561 prog='gdbremote',
1562 usage=usage)
1563 parser.add_option(
1564 '-v',
1565 '--verbose',
1566 action='store_true',
1567 dest='verbose',
1568 help='display verbose debug info',
1569 default=False)
1570 parser.add_option(
1571 '-q',
1572 '--quiet',
1573 action='store_true',
1574 dest='quiet',
1575 help='display verbose debug info',
1576 default=False)
1577 parser.add_option(
1578 '-C',
1579 '--color',
1580 action='store_true',
1581 dest='color',
1582 help='add terminal colors',
1583 default=False)
1584 parser.add_option(
1585 '-c',
1586 '--sort-by-count',
1587 action='store_true',
1588 dest='sort_count',
1589 help='display verbose debug info',
1590 default=False)
1591 parser.add_option(
1592 '--crashlog',
1593 type='string',
1594 dest='crashlog',
1595 help='symbolicate using a darwin crash log file',
1596 default=False)
1597 try:
1598 (options, args) = parser.parse_args(sys.argv[1:])
1599 except:
1600 print('error: argument error')
1601 sys.exit(1)
1603 options.colors = TerminalColors(options.color)
1604 options.symbolicator = None
1605 if options.crashlog:
1606 import lldb
1607 lldb.debugger = lldb.SBDebugger.Create()
1608 import lldb.macosx.crashlog
1609 options.symbolicator = lldb.macosx.crashlog.CrashLog(options.crashlog)
1610 print('%s' % (options.symbolicator))
1612 # This script is being run from the command line, create a debugger in case we are
1613 # going to use any debugger functions in our function.
1614 if len(args):
1615 for file in args:
1616 print('#----------------------------------------------------------------------')
1617 print("# GDB remote log file: '%s'" % file)
1618 print('#----------------------------------------------------------------------')
1619 parse_gdb_log_file(file, options)
1620 if options.symbolicator:
1621 print('%s' % (options.symbolicator))
1622 else:
1623 parse_gdb_log(sys.stdin, options)
1625 def __lldb_init_module(debugger, internal_dict):
1626 # This initializer is being run from LLDB in the embedded command interpreter
1627 # Add any commands contained in this module to LLDB
1628 debugger.HandleCommand(
1629 'command script add -o -f gdbremote.start_gdb_log start_gdb_log')
1630 debugger.HandleCommand(
1631 'command script add -o -f gdbremote.stop_gdb_log stop_gdb_log')
1632 print('The "start_gdb_log" and "stop_gdb_log" commands are now installed and ready for use, type "start_gdb_log --help" or "stop_gdb_log --help" for more information')