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