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
19import binascii
20import commands
21import json
22import math
23import optparse
24import os
25import re
26import shlex
27import string
28import sys
29import tempfile
30import xml.etree.ElementTree as ET
31
32#----------------------------------------------------------------------
33# Global variables
34#----------------------------------------------------------------------
35g_log_file = ''
36g_byte_order = 'little'
37g_number_regex = re.compile('^(0x[0-9a-fA-F]+|[0-9]+)')
38g_thread_id_regex = re.compile('^(-1|[0-9a-fA-F]+|0)')
39
40
41class 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
191def 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
240def 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
320def 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
326g_register_infos = list()
327g_max_register_info_name_len = 0
328
329
330class 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
393class 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
564g_thread_suffix_regex = re.compile(';thread:([0-9a-fA-F]+);')
565
566
567def 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
575def 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
581def cmd_stop_reply(options, cmd, args):
582    print "get_last_stop_info()"
583    return False
584
585
586def 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
614def 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
622def 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
642def 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
657def 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
663def 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
699def 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
717def cmd_qC(options, cmd, args):
718    print "query_current_thread_id()"
719
720
721def 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
730def 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
738def rsp_ok_error(rsp):
739    print "rsp: ", rsp
740
741
742def 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
751def 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
760def 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
772def 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
782def cmd_c(options, cmd, args):
783    print "continue()"
784    return False
785
786
787def cmd_s(options, cmd, args):
788    print "step()"
789    return False
790
791
792def 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
825def 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
857def 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
866def cmd_qRegisterInfo(options, cmd, args):
867    print 'query_register_info(reg_num=%i)' % (int(args, 16))
868    return False
869
870
871def 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
889def 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
898def 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
912def 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
918def 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
928def 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
938def 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
947def 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
970def 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
985def 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
995def 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
1001def 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
1011def 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
1016def 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
1036def 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
1057def 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
1063def 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
1084def 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
1095def 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
1106def rsp_read_all_regs(options, cmd, cmd_args, rsp):
1107    packet = Packet(rsp)
1108    dump_all_regs(packet)
1109
1110
1111def 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
1117g_bp_types = ["software_bp", "hardware_bp", "write_wp", "read_wp", "access_wp"]
1118
1119
1120def 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
1137def 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
1145def cmd_kill(options, cmd, args):
1146    print 'kill_process()'
1147    return False
1148
1149
1150def cmd_jThreadsInfo(options, cmd, args):
1151    print 'jThreadsInfo()'
1152    return False
1153
1154
1155def cmd_jGetLoadedDynamicLibrariesInfos(options, cmd, args):
1156    print 'jGetLoadedDynamicLibrariesInfos()'
1157    return False
1158
1159
1160def 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
1177def 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
1183def 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
1189gdb_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
1234def 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
1251def parse_gdb_log_file(path, options):
1252    f = open(path)
1253    parse_gdb_log(f, options)
1254    f.close()
1255
1256
1257def 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
1451if __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
1520else:
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'
1530