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