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