]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - examples/python/gdbremote.py
Vendor import of lldb trunk r290819:
[FreeBSD/FreeBSD.git] / examples / python / gdbremote.py
1 #!/usr/bin/python
2
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.
10 #
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 #----------------------------------------------------------------------
18
19 import binascii
20 import commands
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
31
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)')
39
40
41 class TerminalColors:
42     '''Simple terminal colors class'''
43
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
48
49     def reset(self):
50         '''Reset all terminal colors and formatting.'''
51         if self.enabled:
52             return "\x1b[0m"
53         return ''
54
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 ''
63
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 ''
72
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 ''
81
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 ''
90
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 ''
99
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 ''
109
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 ''
119
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 ''
129
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 ''
139
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 ''
149
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 ''
159
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 ''
169
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 ''
179
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 ''
189
190
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
215
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]
226
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
235
236         result.PutCString('error: invalid log file path')
237     result.PutCString(usage)
238
239
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         '-q',
264         '--quiet',
265         action='store_true',
266         dest='quiet',
267         help='display verbose debug info',
268         default=False)
269     parser.add_option(
270         '-C',
271         '--color',
272         action='store_true',
273         dest='color',
274         help='add terminal colors',
275         default=False)
276     parser.add_option(
277         '-c',
278         '--sort-by-count',
279         action='store_true',
280         dest='sort_count',
281         help='display verbose debug info',
282         default=False)
283     parser.add_option(
284         '-s',
285         '--symbolicate',
286         action='store_true',
287         dest='symbolicate',
288         help='symbolicate addresses in log using current "lldb.target"',
289         default=False)
290     try:
291         (options, args) = parser.parse_args(command_args)
292     except:
293         return
294     options.colors = TerminalColors(options.color)
295     options.symbolicator = None
296     if options.symbolicate:
297         if lldb.target:
298             import lldb.utils.symbolication
299             options.symbolicator = lldb.utils.symbolication.Symbolicator()
300             options.symbolicator.target = lldb.target
301         else:
302             print "error: can't symbolicate without a target"
303
304     if not g_log_file:
305         result.PutCString(
306             'error: logging must have been previously enabled with a call to "stop_gdb_log"')
307     elif os.path.exists(g_log_file):
308         if len(args) == 0:
309             debugger.HandleCommand('log disable gdb-remote packets')
310             result.PutCString(
311                 "GDB packet logging disabled. Logged packets are in '%s'" %
312                 g_log_file)
313             parse_gdb_log_file(g_log_file, options)
314         else:
315             result.PutCString(usage)
316     else:
317         print 'error: the GDB packet log file "%s" does not exist' % g_log_file
318
319
320 def is_hex_byte(str):
321     if len(str) == 2:
322         return str[0] in string.hexdigits and str[1] in string.hexdigits
323     return False
324
325 # global register info list
326 g_register_infos = list()
327 g_max_register_info_name_len = 0
328
329
330 class RegisterInfo:
331     """Class that represents register information"""
332
333     def __init__(self, kvp):
334         self.info = dict()
335         for kv in kvp:
336             key = kv[0]
337             value = kv[1]
338             self.info[key] = value
339
340     def name(self):
341         '''Get the name of the register.'''
342         if self.info and 'name' in self.info:
343             return self.info['name']
344         return None
345
346     def bit_size(self):
347         '''Get the size in bits of the register.'''
348         if self.info and 'bitsize' in self.info:
349             return int(self.info['bitsize'])
350         return 0
351
352     def byte_size(self):
353         '''Get the size in bytes of the register.'''
354         return self.bit_size() / 8
355
356     def get_value_from_hex_string(self, hex_str):
357         '''Dump the register value given a native byte order encoded hex ASCII byte string.'''
358         encoding = self.info['encoding']
359         bit_size = self.bit_size()
360         packet = Packet(hex_str)
361         if encoding == 'uint':
362             uval = packet.get_hex_uint(g_byte_order)
363             if bit_size == 8:
364                 return '0x%2.2x' % (uval)
365             elif bit_size == 16:
366                 return '0x%4.4x' % (uval)
367             elif bit_size == 32:
368                 return '0x%8.8x' % (uval)
369             elif bit_size == 64:
370                 return '0x%16.16x' % (uval)
371         bytes = list()
372         uval = packet.get_hex_uint8()
373         while uval is not None:
374             bytes.append(uval)
375             uval = packet.get_hex_uint8()
376         value_str = '0x'
377         if g_byte_order == 'little':
378             bytes.reverse()
379         for byte in bytes:
380             value_str += '%2.2x' % byte
381         return '%s' % (value_str)
382
383     def __str__(self):
384         '''Dump the register info key/value pairs'''
385         s = ''
386         for key in self.info.keys():
387             if s:
388                 s += ', '
389             s += "%s=%s " % (key, self.info[key])
390         return s
391
392
393 class Packet:
394     """Class that represents a packet that contains string data"""
395
396     def __init__(self, packet_str):
397         self.str = packet_str
398
399     def peek_char(self):
400         ch = 0
401         if self.str:
402             ch = self.str[0]
403         return ch
404
405     def get_char(self):
406         ch = 0
407         if self.str:
408             ch = self.str[0]
409             self.str = self.str[1:]
410         return ch
411
412     def skip_exact_string(self, s):
413         if self.str and self.str.startswith(s):
414             self.str = self.str[len(s):]
415             return True
416         else:
417             return False
418
419     def get_thread_id(self, fail_value=-1):
420         match = g_number_regex.match(self.str)
421         if match:
422             number_str = match.group(1)
423             self.str = self.str[len(number_str):]
424             return int(number_str, 0)
425         else:
426             return fail_value
427
428     def get_hex_uint8(self):
429         if self.str and len(self.str) >= 2 and self.str[
430                 0] in string.hexdigits and self.str[1] in string.hexdigits:
431             uval = int(self.str[0:2], 16)
432             self.str = self.str[2:]
433             return uval
434         return None
435
436     def get_hex_uint16(self, byte_order):
437         uval = 0
438         if byte_order == 'big':
439             uval |= self.get_hex_uint8() << 8
440             uval |= self.get_hex_uint8()
441         else:
442             uval |= self.get_hex_uint8()
443             uval |= self.get_hex_uint8() << 8
444         return uval
445
446     def get_hex_uint32(self, byte_order):
447         uval = 0
448         if byte_order == 'big':
449             uval |= self.get_hex_uint8() << 24
450             uval |= self.get_hex_uint8() << 16
451             uval |= self.get_hex_uint8() << 8
452             uval |= self.get_hex_uint8()
453         else:
454             uval |= self.get_hex_uint8()
455             uval |= self.get_hex_uint8() << 8
456             uval |= self.get_hex_uint8() << 16
457             uval |= self.get_hex_uint8() << 24
458         return uval
459
460     def get_hex_uint64(self, byte_order):
461         uval = 0
462         if byte_order == 'big':
463             uval |= self.get_hex_uint8() << 56
464             uval |= self.get_hex_uint8() << 48
465             uval |= self.get_hex_uint8() << 40
466             uval |= self.get_hex_uint8() << 32
467             uval |= self.get_hex_uint8() << 24
468             uval |= self.get_hex_uint8() << 16
469             uval |= self.get_hex_uint8() << 8
470             uval |= self.get_hex_uint8()
471         else:
472             uval |= self.get_hex_uint8()
473             uval |= self.get_hex_uint8() << 8
474             uval |= self.get_hex_uint8() << 16
475             uval |= self.get_hex_uint8() << 24
476             uval |= self.get_hex_uint8() << 32
477             uval |= self.get_hex_uint8() << 40
478             uval |= self.get_hex_uint8() << 48
479             uval |= self.get_hex_uint8() << 56
480         return uval
481
482     def get_number(self, fail_value=-1):
483         '''Get a number from the packet. The number must be in big endian format and should be parsed
484         according to its prefix (starts with "0x" means hex, starts with "0" means octal, starts with
485         [1-9] means decimal, etc)'''
486         match = g_number_regex.match(self.str)
487         if match:
488             number_str = match.group(1)
489             self.str = self.str[len(number_str):]
490             return int(number_str, 0)
491         else:
492             return fail_value
493
494     def get_hex_ascii_str(self, n=0):
495         hex_chars = self.get_hex_chars(n)
496         if hex_chars:
497             return binascii.unhexlify(hex_chars)
498         else:
499             return None
500
501     def get_hex_chars(self, n=0):
502         str_len = len(self.str)
503         if n == 0:
504             # n was zero, so we need to determine all hex chars and
505             # stop when we hit the end of the string of a non-hex character
506             while n < str_len and self.str[n] in string.hexdigits:
507                 n = n + 1
508         else:
509             if n > str_len:
510                 return None  # Not enough chars
511             # Verify all chars are hex if a length was specified
512             for i in range(n):
513                 if self.str[i] not in string.hexdigits:
514                     return None  # Not all hex digits
515         if n == 0:
516             return None
517         hex_str = self.str[0:n]
518         self.str = self.str[n:]
519         return hex_str
520
521     def get_hex_uint(self, byte_order, n=0):
522         if byte_order == 'big':
523             hex_str = self.get_hex_chars(n)
524             if hex_str is None:
525                 return None
526             return int(hex_str, 16)
527         else:
528             uval = self.get_hex_uint8()
529             if uval is None:
530                 return None
531             uval_result = 0
532             shift = 0
533             while uval is not None:
534                 uval_result |= (uval << shift)
535                 shift += 8
536                 uval = self.get_hex_uint8()
537             return uval_result
538
539     def get_key_value_pairs(self):
540         kvp = list()
541         if ';' in self.str:
542             key_value_pairs = string.split(self.str, ';')
543             for key_value_pair in key_value_pairs:
544                 if len(key_value_pair):
545                     kvp.append(string.split(key_value_pair, ':'))
546         return kvp
547
548     def split(self, ch):
549         return string.split(self.str, ch)
550
551     def split_hex(self, ch, byte_order):
552         hex_values = list()
553         strings = string.split(self.str, ch)
554         for str in strings:
555             hex_values.append(Packet(str).get_hex_uint(byte_order))
556         return hex_values
557
558     def __str__(self):
559         return self.str
560
561     def __len__(self):
562         return len(self.str)
563
564 g_thread_suffix_regex = re.compile(';thread:([0-9a-fA-F]+);')
565
566
567 def get_thread_from_thread_suffix(str):
568     if str:
569         match = g_thread_suffix_regex.match(str)
570         if match:
571             return int(match.group(1), 16)
572     return None
573
574
575 def cmd_qThreadStopInfo(options, cmd, args):
576     packet = Packet(args)
577     tid = packet.get_hex_uint('big')
578     print "get_thread_stop_info  (tid = 0x%x)" % (tid)
579
580
581 def cmd_stop_reply(options, cmd, args):
582     print "get_last_stop_info()"
583     return False
584
585
586 def rsp_stop_reply(options, cmd, cmd_args, rsp):
587     global g_byte_order
588     packet = Packet(rsp)
589     stop_type = packet.get_char()
590     if stop_type == 'T' or stop_type == 'S':
591         signo = packet.get_hex_uint8()
592         key_value_pairs = packet.get_key_value_pairs()
593         for key_value_pair in key_value_pairs:
594             key = key_value_pair[0]
595             if is_hex_byte(key):
596                 reg_num = Packet(key).get_hex_uint8()
597                 if reg_num < len(g_register_infos):
598                     reg_info = g_register_infos[reg_num]
599                     key_value_pair[0] = reg_info.name()
600                     key_value_pair[1] = reg_info.get_value_from_hex_string(
601                         key_value_pair[1])
602             elif key == 'jthreads' or key == 'jstopinfo':
603                 key_value_pair[1] = binascii.unhexlify(key_value_pair[1])
604         key_value_pairs.insert(0, ['signal', signo])
605         print 'stop_reply():'
606         dump_key_value_pairs(key_value_pairs)
607     elif stop_type == 'W':
608         exit_status = packet.get_hex_uint8()
609         print 'stop_reply(): exit (status=%i)' % exit_status
610     elif stop_type == 'O':
611         print 'stop_reply(): stdout = "%s"' % packet.str
612
613
614 def cmd_unknown_packet(options, cmd, args):
615     if args:
616         print "cmd: %s, args: %s", cmd, args
617     else:
618         print "cmd: %s", cmd
619     return False
620
621
622 def cmd_qSymbol(options, cmd, args):
623     if args == ':':
624         print 'ready to serve symbols'
625     else:
626         packet = Packet(args)
627         symbol_addr = packet.get_hex_uint('big')
628         if symbol_addr is None:
629             if packet.skip_exact_string(':'):
630                 symbol_name = packet.get_hex_ascii_str()
631                 print 'lookup_symbol("%s") -> symbol not available yet' % (symbol_name)
632             else:
633                 print 'error: bad command format'
634         else:
635             if packet.skip_exact_string(':'):
636                 symbol_name = packet.get_hex_ascii_str()
637                 print 'lookup_symbol("%s") -> 0x%x' % (symbol_name, symbol_addr)
638             else:
639                 print 'error: bad command format'
640
641
642 def rsp_qSymbol(options, cmd, cmd_args, rsp):
643     if len(rsp) == 0:
644         print "Unsupported"
645     else:
646         if rsp == "OK":
647             print "No more symbols to lookup"
648         else:
649             packet = Packet(rsp)
650             if packet.skip_exact_string("qSymbol:"):
651                 symbol_name = packet.get_hex_ascii_str()
652                 print 'lookup_symbol("%s")' % (symbol_name)
653             else:
654                 print 'error: response string should start with "qSymbol:": respnse is "%s"' % (rsp)
655
656
657 def cmd_qXfer(options, cmd, args):
658     # $qXfer:features:read:target.xml:0,1ffff#14
659     print "read target special data %s" % (args)
660     return True
661
662
663 def rsp_qXfer(options, cmd, cmd_args, rsp):
664     data = string.split(cmd_args, ':')
665     if data[0] == 'features':
666         if data[1] == 'read':
667             filename, extension = os.path.splitext(data[2])
668             if extension == '.xml':
669                 response = Packet(rsp)
670                 xml_string = response.get_hex_ascii_str()
671                 ch = xml_string[0]
672                 if ch == 'l':
673                     xml_string = xml_string[1:]
674                     xml_root = ET.fromstring(xml_string)
675                     for reg_element in xml_root.findall("./feature/reg"):
676                         if not 'value_regnums' in reg_element.attrib:
677                             reg_info = RegisterInfo([])
678                             if 'name' in reg_element.attrib:
679                                 reg_info.info[
680                                     'name'] = reg_element.attrib['name']
681                             else:
682                                 reg_info.info['name'] = 'unspecified'
683                             if 'encoding' in reg_element.attrib:
684                                 reg_info.info['encoding'] = reg_element.attrib[
685                                     'encoding']
686                             else:
687                                 reg_info.info['encoding'] = 'uint'
688                             if 'offset' in reg_element.attrib:
689                                 reg_info.info[
690                                     'offset'] = reg_element.attrib['offset']
691                             if 'bitsize' in reg_element.attrib:
692                                 reg_info.info[
693                                     'bitsize'] = reg_element.attrib['bitsize']
694                             g_register_infos.append(reg_info)
695                     print 'XML for "%s":' % (data[2])
696                     ET.dump(xml_root)
697
698
699 def cmd_A(options, cmd, args):
700     print 'launch process:'
701     packet = Packet(args)
702     while True:
703         arg_len = packet.get_number()
704         if arg_len == -1:
705             break
706         if not packet.skip_exact_string(','):
707             break
708         arg_idx = packet.get_number()
709         if arg_idx == -1:
710             break
711         if not packet.skip_exact_string(','):
712             break
713         arg_value = packet.get_hex_ascii_str(arg_len)
714         print 'argv[%u] = "%s"' % (arg_idx, arg_value)
715
716
717 def cmd_qC(options, cmd, args):
718     print "query_current_thread_id()"
719
720
721 def rsp_qC(options, cmd, cmd_args, rsp):
722     packet = Packet(rsp)
723     if packet.skip_exact_string("QC"):
724         tid = packet.get_thread_id()
725         print "current_thread_id = %#x" % (tid)
726     else:
727         print "current_thread_id = old thread ID"
728
729
730 def cmd_query_packet(options, cmd, args):
731     if args:
732         print "%s%s" % (cmd, args)
733     else:
734         print "%s" % (cmd)
735     return False
736
737
738 def rsp_ok_error(rsp):
739     print "rsp: ", rsp
740
741
742 def rsp_ok_means_supported(options, cmd, cmd_args, rsp):
743     if rsp == 'OK':
744         print "%s%s is supported" % (cmd, cmd_args)
745     elif rsp == '':
746         print "%s%s is not supported" % (cmd, cmd_args)
747     else:
748         print "%s%s -> %s" % (cmd, cmd_args, rsp)
749
750
751 def rsp_ok_means_success(options, cmd, cmd_args, rsp):
752     if rsp == 'OK':
753         print "success"
754     elif rsp == '':
755         print "%s%s is not supported" % (cmd, cmd_args)
756     else:
757         print "%s%s -> %s" % (cmd, cmd_args, rsp)
758
759
760 def dump_key_value_pairs(key_value_pairs):
761     max_key_len = 0
762     for key_value_pair in key_value_pairs:
763         key_len = len(key_value_pair[0])
764         if max_key_len < key_len:
765             max_key_len = key_len
766     for key_value_pair in key_value_pairs:
767         key = key_value_pair[0]
768         value = key_value_pair[1]
769         print "%*s = %s" % (max_key_len, key, value)
770
771
772 def rsp_dump_key_value_pairs(options, cmd, cmd_args, rsp):
773     if rsp:
774         print '%s response:' % (cmd)
775         packet = Packet(rsp)
776         key_value_pairs = packet.get_key_value_pairs()
777         dump_key_value_pairs(key_value_pairs)
778     else:
779         print "not supported"
780
781
782 def cmd_c(options, cmd, args):
783     print "continue()"
784     return False
785
786
787 def cmd_s(options, cmd, args):
788     print "step()"
789     return False
790
791
792 def cmd_vCont(options, cmd, args):
793     if args == '?':
794         print "%s: get supported extended continue modes" % (cmd)
795     else:
796         got_other_threads = 0
797         s = ''
798         for thread_action in string.split(args[1:], ';'):
799             (short_action, thread) = string.split(thread_action, ':')
800             tid = int(thread, 16)
801             if short_action == 'c':
802                 action = 'continue'
803             elif short_action == 's':
804                 action = 'step'
805             elif short_action[0] == 'C':
806                 action = 'continue with signal 0x%s' % (short_action[1:])
807             elif short_action == 'S':
808                 action = 'step with signal 0x%s' % (short_action[1:])
809             else:
810                 action = short_action
811             if s:
812                 s += ', '
813             if tid == -1:
814                 got_other_threads = 1
815                 s += 'other-threads:'
816             else:
817                 s += 'thread 0x%4.4x: %s' % (tid, action)
818         if got_other_threads:
819             print "extended_continue (%s)" % (s)
820         else:
821             print "extended_continue (%s, other-threads: suspend)" % (s)
822     return False
823
824
825 def rsp_vCont(options, cmd, cmd_args, rsp):
826     if cmd_args == '?':
827         # Skip the leading 'vCont;'
828         rsp = rsp[6:]
829         modes = string.split(rsp, ';')
830         s = "%s: supported extended continue modes include: " % (cmd)
831
832         for i, mode in enumerate(modes):
833             if i:
834                 s += ', '
835             if mode == 'c':
836                 s += 'continue'
837             elif mode == 'C':
838                 s += 'continue with signal'
839             elif mode == 's':
840                 s += 'step'
841             elif mode == 'S':
842                 s += 'step with signal'
843             else:
844                 s += 'unrecognized vCont mode: ', mode
845         print s
846     elif rsp:
847         if rsp[0] == 'T' or rsp[0] == 'S' or rsp[0] == 'W' or rsp[0] == 'X':
848             rsp_stop_reply(options, cmd, cmd_args, rsp)
849             return
850         if rsp[0] == 'O':
851             print "stdout: %s" % (rsp)
852             return
853     else:
854         print "not supported (cmd = '%s', args = '%s', rsp = '%s')" % (cmd, cmd_args, rsp)
855
856
857 def cmd_vAttach(options, cmd, args):
858     (extra_command, args) = string.split(args, ';')
859     if extra_command:
860         print "%s%s(%s)" % (cmd, extra_command, args)
861     else:
862         print "attach(pid = %u)" % int(args, 16)
863     return False
864
865
866 def cmd_qRegisterInfo(options, cmd, args):
867     print 'query_register_info(reg_num=%i)' % (int(args, 16))
868     return False
869
870
871 def rsp_qRegisterInfo(options, cmd, cmd_args, rsp):
872     global g_max_register_info_name_len
873     print 'query_register_info(reg_num=%i):' % (int(cmd_args, 16)),
874     if len(rsp) == 3 and rsp[0] == 'E':
875         g_max_register_info_name_len = 0
876         for reg_info in g_register_infos:
877             name_len = len(reg_info.name())
878             if g_max_register_info_name_len < name_len:
879                 g_max_register_info_name_len = name_len
880         print' DONE'
881     else:
882         packet = Packet(rsp)
883         reg_info = RegisterInfo(packet.get_key_value_pairs())
884         g_register_infos.append(reg_info)
885         print reg_info
886     return False
887
888
889 def cmd_qThreadInfo(options, cmd, args):
890     if cmd == 'qfThreadInfo':
891         query_type = 'first'
892     else:
893         query_type = 'subsequent'
894     print 'get_current_thread_list(type=%s)' % (query_type)
895     return False
896
897
898 def rsp_qThreadInfo(options, cmd, cmd_args, rsp):
899     packet = Packet(rsp)
900     response_type = packet.get_char()
901     if response_type == 'm':
902         tids = packet.split_hex(';', 'big')
903         for i, tid in enumerate(tids):
904             if i:
905                 print ',',
906             print '0x%x' % (tid),
907         print
908     elif response_type == 'l':
909         print 'END'
910
911
912 def rsp_hex_big_endian(options, cmd, cmd_args, rsp):
913     packet = Packet(rsp)
914     uval = packet.get_hex_uint('big')
915     print '%s: 0x%x' % (cmd, uval)
916
917
918 def cmd_read_mem_bin(options, cmd, args):
919     # x0x7fff5fc39200,0x200
920     packet = Packet(args)
921     addr = packet.get_number()
922     comma = packet.get_char()
923     size = packet.get_number()
924     print 'binary_read_memory (addr = 0x%16.16x, size = %u)' % (addr, size)
925     return False
926
927
928 def rsp_mem_bin_bytes(options, cmd, cmd_args, rsp):
929     packet = Packet(cmd_args)
930     addr = packet.get_number()
931     comma = packet.get_char()
932     size = packet.get_number()
933     print 'memory:'
934     if size > 0:
935         dump_hex_memory_buffer(addr, rsp)
936
937
938 def cmd_read_memory(options, cmd, args):
939     packet = Packet(args)
940     addr = packet.get_hex_uint('big')
941     comma = packet.get_char()
942     size = packet.get_hex_uint('big')
943     print 'read_memory (addr = 0x%16.16x, size = %u)' % (addr, size)
944     return False
945
946
947 def dump_hex_memory_buffer(addr, hex_byte_str):
948     packet = Packet(hex_byte_str)
949     idx = 0
950     ascii = ''
951     uval = packet.get_hex_uint8()
952     while uval is not None:
953         if ((idx % 16) == 0):
954             if ascii:
955                 print '  ', ascii
956                 ascii = ''
957             print '0x%x:' % (addr + idx),
958         print '%2.2x' % (uval),
959         if 0x20 <= uval and uval < 0x7f:
960             ascii += '%c' % uval
961         else:
962             ascii += '.'
963         uval = packet.get_hex_uint8()
964         idx = idx + 1
965     if ascii:
966         print '  ', ascii
967         ascii = ''
968
969
970 def cmd_write_memory(options, cmd, args):
971     packet = Packet(args)
972     addr = packet.get_hex_uint('big')
973     if packet.get_char() != ',':
974         print 'error: invalid write memory command (missing comma after address)'
975         return
976     size = packet.get_hex_uint('big')
977     if packet.get_char() != ':':
978         print 'error: invalid write memory command (missing colon after size)'
979         return
980     print 'write_memory (addr = 0x%16.16x, size = %u, data:' % (addr, size)
981     dump_hex_memory_buffer(addr, packet.str)
982     return False
983
984
985 def cmd_alloc_memory(options, cmd, args):
986     packet = Packet(args)
987     byte_size = packet.get_hex_uint('big')
988     if packet.get_char() != ',':
989         print 'error: invalid allocate memory command (missing comma after address)'
990         return
991     print 'allocate_memory (byte-size = %u (0x%x), permissions = %s)' % (byte_size, byte_size, packet.str)
992     return False
993
994
995 def rsp_alloc_memory(options, cmd, cmd_args, rsp):
996     packet = Packet(rsp)
997     addr = packet.get_hex_uint('big')
998     print 'addr = 0x%x' % addr
999
1000
1001 def cmd_dealloc_memory(options, cmd, args):
1002     packet = Packet(args)
1003     addr = packet.get_hex_uint('big')
1004     if packet.get_char() != ',':
1005         print 'error: invalid allocate memory command (missing comma after address)'
1006     else:
1007         print 'deallocate_memory (addr = 0x%x, permissions = %s)' % (addr, packet.str)
1008     return False
1009
1010
1011 def rsp_memory_bytes(options, cmd, cmd_args, rsp):
1012     addr = Packet(cmd_args).get_hex_uint('big')
1013     dump_hex_memory_buffer(addr, rsp)
1014
1015
1016 def get_register_name_equal_value(options, reg_num, hex_value_str):
1017     if reg_num < len(g_register_infos):
1018         reg_info = g_register_infos[reg_num]
1019         value_str = reg_info.get_value_from_hex_string(hex_value_str)
1020         s = reg_info.name() + ' = '
1021         if options.symbolicator:
1022             symbolicated_addresses = options.symbolicator.symbolicate(
1023                 int(value_str, 0))
1024             if symbolicated_addresses:
1025                 s += options.colors.magenta()
1026                 s += '%s' % symbolicated_addresses[0]
1027                 s += options.colors.reset()
1028                 return s
1029         s += value_str
1030         return s
1031     else:
1032         reg_value = Packet(hex_value_str).get_hex_uint(g_byte_order)
1033         return 'reg(%u) = 0x%x' % (reg_num, reg_value)
1034
1035
1036 def cmd_read_one_reg(options, cmd, args):
1037     packet = Packet(args)
1038     reg_num = packet.get_hex_uint('big')
1039     tid = get_thread_from_thread_suffix(packet.str)
1040     name = None
1041     if reg_num < len(g_register_infos):
1042         name = g_register_infos[reg_num].name()
1043     if packet.str:
1044         packet.get_char()  # skip ;
1045         thread_info = packet.get_key_value_pairs()
1046         tid = int(thread_info[0][1], 16)
1047     s = 'read_register (reg_num=%u' % reg_num
1048     if name:
1049         s += ' (%s)' % (name)
1050     if tid is not None:
1051         s += ', tid = 0x%4.4x' % (tid)
1052     s += ')'
1053     print s
1054     return False
1055
1056
1057 def rsp_read_one_reg(options, cmd, cmd_args, rsp):
1058     packet = Packet(cmd_args)
1059     reg_num = packet.get_hex_uint('big')
1060     print get_register_name_equal_value(options, reg_num, rsp)
1061
1062
1063 def cmd_write_one_reg(options, cmd, args):
1064     packet = Packet(args)
1065     reg_num = packet.get_hex_uint('big')
1066     if packet.get_char() != '=':
1067         print 'error: invalid register write packet'
1068     else:
1069         name = None
1070         hex_value_str = packet.get_hex_chars()
1071         tid = get_thread_from_thread_suffix(packet.str)
1072         s = 'write_register (reg_num=%u' % reg_num
1073         if name:
1074             s += ' (%s)' % (name)
1075         s += ', value = '
1076         s += get_register_name_equal_value(options, reg_num, hex_value_str)
1077         if tid is not None:
1078             s += ', tid = 0x%4.4x' % (tid)
1079         s += ')'
1080         print s
1081     return False
1082
1083
1084 def dump_all_regs(packet):
1085     for reg_info in g_register_infos:
1086         nibble_size = reg_info.bit_size() / 4
1087         hex_value_str = packet.get_hex_chars(nibble_size)
1088         if hex_value_str is not None:
1089             value = reg_info.get_value_from_hex_string(hex_value_str)
1090             print '%*s = %s' % (g_max_register_info_name_len, reg_info.name(), value)
1091         else:
1092             return
1093
1094
1095 def cmd_read_all_regs(cmd, cmd_args):
1096     packet = Packet(cmd_args)
1097     packet.get_char()  # toss the 'g' command character
1098     tid = get_thread_from_thread_suffix(packet.str)
1099     if tid is not None:
1100         print 'read_all_register(thread = 0x%4.4x)' % tid
1101     else:
1102         print 'read_all_register()'
1103     return False
1104
1105
1106 def rsp_read_all_regs(options, cmd, cmd_args, rsp):
1107     packet = Packet(rsp)
1108     dump_all_regs(packet)
1109
1110
1111 def cmd_write_all_regs(options, cmd, args):
1112     packet = Packet(args)
1113     print 'write_all_registers()'
1114     dump_all_regs(packet)
1115     return False
1116
1117 g_bp_types = ["software_bp", "hardware_bp", "write_wp", "read_wp", "access_wp"]
1118
1119
1120 def cmd_bp(options, cmd, args):
1121     if cmd == 'Z':
1122         s = 'set_'
1123     else:
1124         s = 'clear_'
1125     packet = Packet(args)
1126     bp_type = packet.get_hex_uint('big')
1127     packet.get_char()  # Skip ,
1128     bp_addr = packet.get_hex_uint('big')
1129     packet.get_char()  # Skip ,
1130     bp_size = packet.get_hex_uint('big')
1131     s += g_bp_types[bp_type]
1132     s += " (addr = 0x%x, size = %u)" % (bp_addr, bp_size)
1133     print s
1134     return False
1135
1136
1137 def cmd_mem_rgn_info(options, cmd, args):
1138     packet = Packet(args)
1139     packet.get_char()  # skip ':' character
1140     addr = packet.get_hex_uint('big')
1141     print 'get_memory_region_info (addr=0x%x)' % (addr)
1142     return False
1143
1144
1145 def cmd_kill(options, cmd, args):
1146     print 'kill_process()'
1147     return False
1148
1149
1150 def cmd_jThreadsInfo(options, cmd, args):
1151     print 'jThreadsInfo()'
1152     return False
1153
1154
1155 def cmd_jGetLoadedDynamicLibrariesInfos(options, cmd, args):
1156     print 'jGetLoadedDynamicLibrariesInfos()'
1157     return False
1158
1159
1160 def decode_packet(s, start_index=0):
1161     # print '\ndecode_packet("%s")' % (s[start_index:])
1162     index = s.find('}', start_index)
1163     have_escapes = index != -1
1164     if have_escapes:
1165         normal_s = s[start_index:index]
1166     else:
1167         normal_s = s[start_index:]
1168     # print 'normal_s = "%s"' % (normal_s)
1169     if have_escapes:
1170         escape_char = '%c' % (ord(s[index + 1]) ^ 0x20)
1171         # print 'escape_char for "%s" = %c' % (s[index:index+2], escape_char)
1172         return normal_s + escape_char + decode_packet(s, index + 2)
1173     else:
1174         return normal_s
1175
1176
1177 def rsp_json(options, cmd, cmd_args, rsp):
1178     print '%s() reply:' % (cmd)
1179     json_tree = json.loads(rsp)
1180     print json.dumps(json_tree, indent=4, separators=(',', ': '))
1181
1182
1183 def rsp_jGetLoadedDynamicLibrariesInfos(options, cmd, cmd_args, rsp):
1184     if cmd_args:
1185         rsp_json(options, cmd, cmd_args, rsp)
1186     else:
1187         rsp_ok_means_supported(options, cmd, cmd_args, rsp)
1188
1189 gdb_remote_commands = {
1190     '\\?': {'cmd': cmd_stop_reply, 'rsp': rsp_stop_reply, 'name': "stop reply pacpket"},
1191     'qThreadStopInfo': {'cmd': cmd_qThreadStopInfo, 'rsp': rsp_stop_reply, 'name': "stop reply pacpket"},
1192     'QStartNoAckMode': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query if no ack mode is supported"},
1193     'QThreadSuffixSupported': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query if thread suffix is supported"},
1194     'QListThreadsInStopReply': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query if threads in stop reply packets are supported"},
1195     'QSetDetachOnError': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_success, 'name': "set if we should detach on error"},
1196     'QSetDisableASLR': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_success, 'name': "set if we should disable ASLR"},
1197     'qLaunchSuccess': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_success, 'name': "check on launch success for the A packet"},
1198     'A': {'cmd': cmd_A, 'rsp': rsp_ok_means_success, 'name': "launch process"},
1199     'QLaunchArch': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "set if we should disable ASLR"},
1200     'qVAttachOrWaitSupported': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "set the launch architecture"},
1201     'qHostInfo': {'cmd': cmd_query_packet, 'rsp': rsp_dump_key_value_pairs, 'name': "get host information"},
1202     'qC': {'cmd': cmd_qC, 'rsp': rsp_qC, 'name': "return the current thread ID"},
1203     'vCont': {'cmd': cmd_vCont, 'rsp': rsp_vCont, 'name': "extended continue command"},
1204     'vAttach': {'cmd': cmd_vAttach, 'rsp': rsp_stop_reply, 'name': "attach to process"},
1205     'c': {'cmd': cmd_c, 'rsp': rsp_stop_reply, 'name': "continue"},
1206     's': {'cmd': cmd_s, 'rsp': rsp_stop_reply, 'name': "step"},
1207     'qRegisterInfo': {'cmd': cmd_qRegisterInfo, 'rsp': rsp_qRegisterInfo, 'name': "query register info"},
1208     'qfThreadInfo': {'cmd': cmd_qThreadInfo, 'rsp': rsp_qThreadInfo, 'name': "get current thread list"},
1209     'qsThreadInfo': {'cmd': cmd_qThreadInfo, 'rsp': rsp_qThreadInfo, 'name': "get current thread list"},
1210     'qShlibInfoAddr': {'cmd': cmd_query_packet, 'rsp': rsp_hex_big_endian, 'name': "get shared library info address"},
1211     'qMemoryRegionInfo': {'cmd': cmd_mem_rgn_info, 'rsp': rsp_dump_key_value_pairs, 'name': "get memory region information"},
1212     'qProcessInfo': {'cmd': cmd_query_packet, 'rsp': rsp_dump_key_value_pairs, 'name': "get process info"},
1213     'qSupported': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query supported"},
1214     'qXfer:': {'cmd': cmd_qXfer, 'rsp': rsp_qXfer, 'name': "qXfer"},
1215     'qSymbol:': {'cmd': cmd_qSymbol, 'rsp': rsp_qSymbol, 'name': "qSymbol"},
1216     'x': {'cmd': cmd_read_mem_bin, 'rsp': rsp_mem_bin_bytes, 'name': "read memory binary"},
1217     'X': {'cmd': cmd_write_memory, 'rsp': rsp_ok_means_success, 'name': "write memory binary"},
1218     'm': {'cmd': cmd_read_memory, 'rsp': rsp_memory_bytes, 'name': "read memory"},
1219     'M': {'cmd': cmd_write_memory, 'rsp': rsp_ok_means_success, 'name': "write memory"},
1220     '_M': {'cmd': cmd_alloc_memory, 'rsp': rsp_alloc_memory, 'name': "allocate memory"},
1221     '_m': {'cmd': cmd_dealloc_memory, 'rsp': rsp_ok_means_success, 'name': "deallocate memory"},
1222     'p': {'cmd': cmd_read_one_reg, 'rsp': rsp_read_one_reg, 'name': "read single register"},
1223     'P': {'cmd': cmd_write_one_reg, 'rsp': rsp_ok_means_success, 'name': "write single register"},
1224     'g': {'cmd': cmd_read_all_regs, 'rsp': rsp_read_all_regs, 'name': "read all registers"},
1225     'G': {'cmd': cmd_write_all_regs, 'rsp': rsp_ok_means_success, 'name': "write all registers"},
1226     'z': {'cmd': cmd_bp, 'rsp': rsp_ok_means_success, 'name': "clear breakpoint or watchpoint"},
1227     'Z': {'cmd': cmd_bp, 'rsp': rsp_ok_means_success, 'name': "set breakpoint or watchpoint"},
1228     'k': {'cmd': cmd_kill, 'rsp': rsp_stop_reply, 'name': "kill process"},
1229     'jThreadsInfo': {'cmd': cmd_jThreadsInfo, 'rsp': rsp_json, 'name': "JSON get all threads info"},
1230     'jGetLoadedDynamicLibrariesInfos:': {'cmd': cmd_jGetLoadedDynamicLibrariesInfos, 'rsp': rsp_jGetLoadedDynamicLibrariesInfos, 'name': 'JSON get loaded dynamic libraries'},
1231 }
1232
1233
1234 def calculate_mean_and_standard_deviation(floats):
1235     sum = 0.0
1236     count = len(floats)
1237     if count == 0:
1238         return (0.0, 0.0)
1239     for f in floats:
1240         sum += f
1241     mean = sum / count
1242     accum = 0.0
1243     for f in floats:
1244         delta = f - mean
1245         accum += delta * delta
1246
1247     std_dev = math.sqrt(accum / (count - 1))
1248     return (mean, std_dev)
1249
1250
1251 def parse_gdb_log_file(path, options):
1252     f = open(path)
1253     parse_gdb_log(f, options)
1254     f.close()
1255
1256
1257 def parse_gdb_log(file, options):
1258     '''Parse a GDB log file that was generated by enabling logging with:
1259     (lldb) log enable --threadsafe --timestamp --file <FILE> gdb-remote packets
1260     This log file will contain timestamps and this function will then normalize
1261     those packets to be relative to the first value timestamp that is found and
1262     show delta times between log lines and also keep track of how long it takes
1263     for GDB remote commands to make a send/receive round trip. This can be
1264     handy when trying to figure out why some operation in the debugger is taking
1265     a long time during a preset set of debugger commands.'''
1266
1267     tricky_commands = ['qRegisterInfo']
1268     timestamp_regex = re.compile('(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$')
1269     packet_name_regex = re.compile('([A-Za-z_]+)[^a-z]')
1270     packet_transmit_name_regex = re.compile(
1271         '(?P<direction>send|read) packet: (?P<packet>.*)')
1272     packet_contents_name_regex = re.compile('\$([^#]+)#[0-9a-fA-F]{2}')
1273     packet_checksum_regex = re.compile('.*#[0-9a-fA-F]{2}$')
1274     packet_names_regex_str = '(' + \
1275         '|'.join(gdb_remote_commands.keys()) + ')(.*)'
1276     packet_names_regex = re.compile(packet_names_regex_str)
1277
1278     base_time = 0.0
1279     last_time = 0.0
1280     packet_send_time = 0.0
1281     packet_total_times = {}
1282     packet_times = []
1283     packet_count = {}
1284     lines = file.read().splitlines()
1285     last_command = None
1286     last_command_args = None
1287     last_command_packet = None
1288     hide_next_response = False
1289     num_lines = len(lines)
1290     skip_count = 0
1291     for (line_index, line) in enumerate(lines):
1292         # See if we need to skip any lines
1293         if skip_count > 0:
1294             skip_count -= 1
1295             continue
1296         m = packet_transmit_name_regex.search(line)
1297         is_command = False
1298         direction = None
1299         if m:
1300             direction = m.group('direction')
1301             is_command = direction == 'send'
1302             packet = m.group('packet')
1303             sys.stdout.write(options.colors.green())
1304             if not options.quiet and not hide_next_response:
1305                 print '#  ', line
1306             sys.stdout.write(options.colors.reset())
1307
1308             # print 'direction = "%s", packet = "%s"' % (direction, packet)
1309
1310             if packet[0] == '+':
1311                 if is_command:
1312                     print '-->',
1313                 else:
1314                     print '<--',
1315                 if not options.quiet:
1316                     print 'ACK'
1317                 continue
1318             elif packet[0] == '-':
1319                 if is_command:
1320                     print '-->',
1321                 else:
1322                     print '<--',
1323                 if not options.quiet:
1324                     print 'NACK'
1325                 continue
1326             elif packet[0] == '$':
1327                 m = packet_contents_name_regex.match(packet)
1328                 if not m and packet[0] == '$':
1329                     multiline_packet = packet
1330                     idx = line_index + 1
1331                     while idx < num_lines:
1332                         if not options.quiet and not hide_next_response:
1333                             print '#  ', lines[idx]
1334                         multiline_packet += lines[idx]
1335                         m = packet_contents_name_regex.match(multiline_packet)
1336                         if m:
1337                             packet = multiline_packet
1338                             skip_count = idx - line_index
1339                             break
1340                         else:
1341                             idx += 1
1342                 if m:
1343                     if is_command:
1344                         print '-->',
1345                     else:
1346                         print '<--',
1347                     contents = decode_packet(m.group(1))
1348                     if is_command:
1349                         hide_next_response = False
1350                         m = packet_names_regex.match(contents)
1351                         if m:
1352                             last_command = m.group(1)
1353                             if last_command == '?':
1354                                 last_command = '\\?'
1355                             packet_name = last_command
1356                             last_command_args = m.group(2)
1357                             last_command_packet = contents
1358                             hide_next_response = gdb_remote_commands[last_command][
1359                                 'cmd'](options, last_command, last_command_args)
1360                         else:
1361                             packet_match = packet_name_regex.match(contents)
1362                             if packet_match:
1363                                 packet_name = packet_match.group(1)
1364                                 for tricky_cmd in tricky_commands:
1365                                     if packet_name.find(tricky_cmd) == 0:
1366                                         packet_name = tricky_cmd
1367                             else:
1368                                 packet_name = contents
1369                             last_command = None
1370                             last_command_args = None
1371                             last_command_packet = None
1372                     elif last_command:
1373                         gdb_remote_commands[last_command]['rsp'](
1374                             options, last_command, last_command_args, contents)
1375                 else:
1376                     print 'error: invalid packet: "', packet, '"'
1377             else:
1378                 print '???'
1379         else:
1380             print '## ', line
1381         match = timestamp_regex.match(line)
1382         if match:
1383             curr_time = float(match.group(2))
1384             if last_time and not is_command:
1385                 delta = curr_time - last_time
1386                 packet_times.append(delta)
1387             delta = 0.0
1388             if base_time:
1389                 delta = curr_time - last_time
1390             else:
1391                 base_time = curr_time
1392
1393             if is_command:
1394                 packet_send_time = curr_time
1395             elif line.find('read packet: $') >= 0 and packet_name:
1396                 if packet_name in packet_total_times:
1397                     packet_total_times[packet_name] += delta
1398                     packet_count[packet_name] += 1
1399                 else:
1400                     packet_total_times[packet_name] = delta
1401                     packet_count[packet_name] = 1
1402                 packet_name = None
1403
1404             if not options or not options.quiet:
1405                 print '%s%.6f %+.6f%s' % (match.group(1), curr_time - base_time, delta, match.group(3))
1406             last_time = curr_time
1407         # else:
1408         #     print line
1409     (average, std_dev) = calculate_mean_and_standard_deviation(packet_times)
1410     if average and std_dev:
1411         print '%u packets with average packet time of %f and standard deviation of %f' % (len(packet_times), average, std_dev)
1412     if packet_total_times:
1413         total_packet_time = 0.0
1414         total_packet_count = 0
1415         for key, vvv in packet_total_times.items():
1416             # print '  key = (%s) "%s"' % (type(key), key)
1417             # print 'value = (%s) %s' % (type(vvv), vvv)
1418             # if type(vvv) == 'float':
1419             total_packet_time += vvv
1420         for key, vvv in packet_count.items():
1421             total_packet_count += vvv
1422
1423         print '#---------------------------------------------------'
1424         print '# Packet timing summary:'
1425         print '# Totals: time = %6f, count = %6d' % (total_packet_time, total_packet_count)
1426         print '#---------------------------------------------------'
1427         print '# Packet                   Time (sec) Percent Count '
1428         print '#------------------------- ---------- ------- ------'
1429         if options and options.sort_count:
1430             res = sorted(
1431                 packet_count,
1432                 key=packet_count.__getitem__,
1433                 reverse=True)
1434         else:
1435             res = sorted(
1436                 packet_total_times,
1437                 key=packet_total_times.__getitem__,
1438                 reverse=True)
1439
1440         if last_time > 0.0:
1441             for item in res:
1442                 packet_total_time = packet_total_times[item]
1443                 packet_percent = (
1444                     packet_total_time / total_packet_time) * 100.0
1445                 if packet_percent >= 10.0:
1446                     print "  %24s %.6f   %.2f%% %6d" % (item, packet_total_time, packet_percent, packet_count[item])
1447                 else:
1448                     print "  %24s %.6f   %.2f%%  %6d" % (item, packet_total_time, packet_percent, packet_count[item])
1449
1450
1451 if __name__ == '__main__':
1452     usage = "usage: gdbremote [options]"
1453     description = '''The command disassembles a GDB remote packet log.'''
1454     parser = optparse.OptionParser(
1455         description=description,
1456         prog='gdbremote',
1457         usage=usage)
1458     parser.add_option(
1459         '-v',
1460         '--verbose',
1461         action='store_true',
1462         dest='verbose',
1463         help='display verbose debug info',
1464         default=False)
1465     parser.add_option(
1466         '-q',
1467         '--quiet',
1468         action='store_true',
1469         dest='quiet',
1470         help='display verbose debug info',
1471         default=False)
1472     parser.add_option(
1473         '-C',
1474         '--color',
1475         action='store_true',
1476         dest='color',
1477         help='add terminal colors',
1478         default=False)
1479     parser.add_option(
1480         '-c',
1481         '--sort-by-count',
1482         action='store_true',
1483         dest='sort_count',
1484         help='display verbose debug info',
1485         default=False)
1486     parser.add_option(
1487         '--crashlog',
1488         type='string',
1489         dest='crashlog',
1490         help='symbolicate using a darwin crash log file',
1491         default=False)
1492     try:
1493         (options, args) = parser.parse_args(sys.argv[1:])
1494     except:
1495         print 'error: argument error'
1496         sys.exit(1)
1497
1498     options.colors = TerminalColors(options.color)
1499     options.symbolicator = None
1500     if options.crashlog:
1501         import lldb
1502         lldb.debugger = lldb.SBDebugger.Create()
1503         import lldb.macosx.crashlog
1504         options.symbolicator = lldb.macosx.crashlog.CrashLog(options.crashlog)
1505         print '%s' % (options.symbolicator)
1506
1507     # This script is being run from the command line, create a debugger in case we are
1508     # going to use any debugger functions in our function.
1509     if len(args):
1510         for file in args:
1511             print '#----------------------------------------------------------------------'
1512             print "# GDB remote log file: '%s'" % file
1513             print '#----------------------------------------------------------------------'
1514             parse_gdb_log_file(file, options)
1515         if options.symbolicator:
1516             print '%s' % (options.symbolicator)
1517     else:
1518         parse_gdb_log(sys.stdin, options)
1519
1520 else:
1521     import lldb
1522     if lldb.debugger:
1523         # This initializer is being run from LLDB in the embedded command interpreter
1524         # Add any commands contained in this module to LLDB
1525         lldb.debugger.HandleCommand(
1526             'command script add -f gdbremote.start_gdb_log start_gdb_log')
1527         lldb.debugger.HandleCommand(
1528             'command script add -f gdbremote.stop_gdb_log stop_gdb_log')
1529         print 'The "start_gdb_log" and "stop_gdb_log" commands are now installed and ready for use, type "start_gdb_log --help" or "stop_gdb_log --help" for more information'