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[43m";
133            else:
134                return "\x1b[33m";
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 not packet.skip_exact_string(','):
629            break
630        arg_idx = packet.get_number()
631        if not packet.skip_exact_string(','):
632            break;
633        arg_value = packet.get_hex_ascii_str(arg_len)
634        print 'argv[%u] = "%s"' % (arg_idx, arg_value)
635
636def cmd_qC(options, cmd, args):
637    print "query_current_thread_id()"
638
639def rsp_qC(options, cmd, cmd_args, rsp):
640    packet = Packet(rsp)
641    if packet.skip_exact_string("QC"):
642        tid = packet.get_thread_id()
643        print "current_thread_id = %#x" % (tid)
644    else:
645        print "current_thread_id = old thread ID"
646
647def cmd_query_packet(options, cmd, args):
648    if args:
649        print "%s%s" % (cmd, args)
650    else:
651        print "%s" % (cmd)
652    return False
653
654def rsp_ok_error(rsp):
655    print "rsp: ", rsp
656
657def rsp_ok_means_supported(options, cmd, cmd_args, rsp):
658    if rsp == 'OK':
659        print "%s%s is supported" % (cmd, cmd_args)
660    elif rsp == '':
661        print "%s%s is not supported" % (cmd, cmd_args)
662    else:
663        print "%s%s -> %s" % (cmd, cmd_args, rsp)
664
665def rsp_ok_means_success(options, cmd, cmd_args, rsp):
666    if rsp == 'OK':
667        print "success"
668    elif rsp == '':
669        print "%s%s is not supported" % (cmd, cmd_args)
670    else:
671        print "%s%s -> %s" % (cmd, cmd_args, rsp)
672
673def dump_key_value_pairs(key_value_pairs):
674    max_key_len = 0
675    for key_value_pair in key_value_pairs:
676        key_len = len(key_value_pair[0])
677        if max_key_len < key_len:
678           max_key_len = key_len
679    for key_value_pair in key_value_pairs:
680        key = key_value_pair[0]
681        value = key_value_pair[1]
682        print "%*s = %s" % (max_key_len, key, value)
683
684def rsp_dump_key_value_pairs(options, cmd, cmd_args, rsp):
685    if rsp:
686        print '%s response:' % (cmd)
687        packet = Packet(rsp)
688        key_value_pairs = packet.get_key_value_pairs()
689        dump_key_value_pairs(key_value_pairs)
690    else:
691        print "not supported"
692
693def cmd_c(options, cmd, args):
694    print "continue()"
695    return False
696
697def cmd_s(options, cmd, args):
698    print "step()"
699    return False
700
701def cmd_vCont(options, cmd, args):
702    if args == '?':
703        print "%s: get supported extended continue modes" % (cmd)
704    else:
705        got_other_threads = 0
706        s = ''
707        for thread_action in string.split(args[1:], ';'):
708            (short_action, thread) = string.split(thread_action, ':')
709            tid = int(thread, 16)
710            if short_action == 'c':
711                action = 'continue'
712            elif short_action == 's':
713                action = 'step'
714            elif short_action[0] == 'C':
715                action = 'continue with signal 0x%s' % (short_action[1:])
716            elif short_action == 'S':
717                action = 'step with signal 0x%s' % (short_action[1:])
718            else:
719                action = short_action
720            if s:
721                s += ', '
722            if tid == -1:
723                got_other_threads = 1
724                s += 'other-threads:'
725            else:
726                s += 'thread 0x%4.4x: %s' % (tid, action)
727        if got_other_threads:
728            print "extended_continue (%s)" % (s)
729        else:
730            print "extended_continue (%s, other-threads: suspend)" % (s)
731    return False
732
733def rsp_vCont(options, cmd, cmd_args, rsp):
734    if cmd_args == '?':
735        # Skip the leading 'vCont;'
736        rsp = rsp[6:]
737        modes = string.split(rsp, ';')
738        s = "%s: supported extended continue modes include: " % (cmd)
739
740        for i, mode in enumerate(modes):
741            if i:
742                s += ', '
743            if mode == 'c':
744                s += 'continue'
745            elif mode == 'C':
746                s += 'continue with signal'
747            elif mode == 's':
748                s += 'step'
749            elif mode == 'S':
750                s += 'step with signal'
751            else:
752                s += 'unrecognized vCont mode: ', mode
753        print s
754    elif rsp:
755        if rsp[0] == 'T' or rsp[0] == 'S' or rsp[0] == 'W' or rsp[0] == 'X':
756            rsp_stop_reply (options, cmd, cmd_args, rsp)
757            return
758        if rsp[0] == 'O':
759            print "stdout: %s" % (rsp)
760            return
761    else:
762        print "not supported (cmd = '%s', args = '%s', rsp = '%s')" % (cmd, cmd_args, rsp)
763
764def cmd_vAttach(options, cmd, args):
765    (extra_command, args) = string.split(args, ';')
766    if extra_command:
767        print "%s%s(%s)" % (cmd, extra_command, args)
768    else:
769        print "attach(pid = %u)" % int(args, 16)
770    return False
771
772
773def cmd_qRegisterInfo(options, cmd, args):
774    print 'query_register_info(reg_num=%i)' % (int(args, 16))
775    return False
776
777def rsp_qRegisterInfo(options, cmd, cmd_args, rsp):
778    global g_max_register_info_name_len
779    print 'query_register_info(reg_num=%i):' % (int(cmd_args, 16)),
780    if len(rsp) == 3 and rsp[0] == 'E':
781        g_max_register_info_name_len = 0
782        for reg_info in g_register_infos:
783            name_len = len(reg_info.name())
784            if g_max_register_info_name_len < name_len:
785                g_max_register_info_name_len = name_len
786        print' DONE'
787    else:
788        packet = Packet(rsp)
789        reg_info = RegisterInfo(packet.get_key_value_pairs())
790        g_register_infos.append(reg_info)
791        print reg_info
792    return False
793
794def cmd_qThreadInfo(options, cmd, args):
795    if cmd == 'qfThreadInfo':
796        query_type = 'first'
797    else:
798        query_type = 'subsequent'
799    print 'get_current_thread_list(type=%s)' % (query_type)
800    return False
801
802def rsp_qThreadInfo(options, cmd, cmd_args, rsp):
803    packet = Packet(rsp)
804    response_type = packet.get_char()
805    if response_type == 'm':
806        tids = packet.split_hex(';', 'big')
807        for i, tid in enumerate(tids):
808            if i:
809                print ',',
810            print '0x%x' % (tid),
811        print
812    elif response_type == 'l':
813        print 'END'
814
815def rsp_hex_big_endian(options, cmd, cmd_args, rsp):
816    packet = Packet(rsp)
817    uval = packet.get_hex_uint('big')
818    print '%s: 0x%x' % (cmd, uval)
819
820def cmd_read_mem_bin(options, cmd, args):
821    # x0x7fff5fc39200,0x200
822    packet = Packet(args)
823    addr = packet.get_number()
824    comma = packet.get_char()
825    size = packet.get_number()
826    print 'binary_read_memory (addr = 0x%16.16x, size = %u)' % (addr, size)
827    return False
828
829def rsp_mem_bin_bytes(options, cmd, cmd_args, rsp):
830    packet = Packet(cmd_args)
831    addr = packet.get_number()
832    comma = packet.get_char()
833    size = packet.get_number()
834    print 'memory:'
835    if size > 0:
836        dump_hex_memory_buffer (addr, rsp)
837
838def cmd_read_memory(options, cmd, args):
839    packet = Packet(args)
840    addr = packet.get_hex_uint('big')
841    comma = packet.get_char()
842    size = packet.get_hex_uint('big')
843    print 'read_memory (addr = 0x%16.16x, size = %u)' % (addr, size)
844    return False
845
846def dump_hex_memory_buffer(addr, hex_byte_str):
847    packet = Packet(hex_byte_str)
848    idx = 0
849    ascii = ''
850    uval = packet.get_hex_uint8()
851    while uval != None:
852        if ((idx % 16) == 0):
853            if ascii:
854                print '  ', ascii
855                ascii = ''
856            print '0x%x:' % (addr + idx),
857        print '%2.2x' % (uval),
858        if 0x20 <= uval and uval < 0x7f:
859            ascii += '%c' % uval
860        else:
861            ascii += '.'
862        uval = packet.get_hex_uint8()
863        idx = idx + 1
864    if ascii:
865        print '  ', ascii
866        ascii = ''
867
868def cmd_write_memory(options, cmd, args):
869    packet = Packet(args)
870    addr = packet.get_hex_uint('big')
871    if packet.get_char() != ',':
872        print 'error: invalid write memory command (missing comma after address)'
873        return
874    size = packet.get_hex_uint('big')
875    if packet.get_char() != ':':
876        print 'error: invalid write memory command (missing colon after size)'
877        return
878    print 'write_memory (addr = 0x%16.16x, size = %u, data:' % (addr, size)
879    dump_hex_memory_buffer (addr, packet.str)
880    return False
881
882def cmd_alloc_memory(options, cmd, args):
883    packet = Packet(args)
884    byte_size = packet.get_hex_uint('big')
885    if packet.get_char() != ',':
886        print 'error: invalid allocate memory command (missing comma after address)'
887        return
888    print 'allocate_memory (byte-size = %u (0x%x), permissions = %s)' % (byte_size, byte_size, packet.str)
889    return False
890
891def rsp_alloc_memory(options, cmd, cmd_args, rsp):
892    packet = Packet(rsp)
893    addr = packet.get_hex_uint('big')
894    print 'addr = 0x%x' % addr
895
896def cmd_dealloc_memory(options, cmd, args):
897    packet = Packet(args)
898    addr = packet.get_hex_uint('big')
899    if packet.get_char() != ',':
900        print 'error: invalid allocate memory command (missing comma after address)'
901    else:
902        print 'deallocate_memory (addr = 0x%x, permissions = %s)' % (addr, packet.str)
903    return False
904def rsp_memory_bytes(options, cmd, cmd_args, rsp):
905    addr = Packet(cmd_args).get_hex_uint('big')
906    dump_hex_memory_buffer (addr, rsp)
907
908def get_register_name_equal_value(options, reg_num, hex_value_str):
909    if reg_num < len(g_register_infos):
910        reg_info = g_register_infos[reg_num]
911        value_str = reg_info.get_value_from_hex_string (hex_value_str)
912        s = reg_info.name() + ' = '
913        if options.symbolicator:
914            symbolicated_addresses = options.symbolicator.symbolicate (int(value_str, 0))
915            if symbolicated_addresses:
916                s += options.colors.magenta()
917                s += '%s' % symbolicated_addresses[0]
918                s += options.colors.reset()
919                return s
920        s += value_str
921        return s
922    else:
923        reg_value = Packet(hex_value_str).get_hex_uint(g_byte_order)
924        return 'reg(%u) = 0x%x' % (reg_num, reg_value)
925
926def cmd_read_one_reg(options, cmd, args):
927    packet = Packet(args)
928    reg_num = packet.get_hex_uint('big')
929    tid = get_thread_from_thread_suffix (packet.str)
930    name = None
931    if reg_num < len(g_register_infos):
932        name = g_register_infos[reg_num].name ()
933    if packet.str:
934        packet.get_char() # skip ;
935        thread_info = packet.get_key_value_pairs()
936        tid = int(thread_info[0][1], 16)
937    s = 'read_register (reg_num=%u' % reg_num
938    if name:
939        s += ' (%s)' % (name)
940    if tid != None:
941        s += ', tid = 0x%4.4x' % (tid)
942    s += ')'
943    print s
944    return False
945
946def rsp_read_one_reg(options, cmd, cmd_args, rsp):
947    packet = Packet(cmd_args)
948    reg_num = packet.get_hex_uint('big')
949    print get_register_name_equal_value (options, reg_num, rsp)
950
951def cmd_write_one_reg(options, cmd, args):
952    packet = Packet(args)
953    reg_num = packet.get_hex_uint('big')
954    if packet.get_char() != '=':
955        print 'error: invalid register write packet'
956    else:
957        name = None
958        hex_value_str = packet.get_hex_chars()
959        tid = get_thread_from_thread_suffix (packet.str)
960        s = 'write_register (reg_num=%u' % reg_num
961        if name:
962            s += ' (%s)' % (name)
963        s += ', value = '
964        s += get_register_name_equal_value(options, reg_num, hex_value_str)
965        if tid != None:
966            s += ', tid = 0x%4.4x' % (tid)
967        s += ')'
968        print s
969    return False
970
971def dump_all_regs(packet):
972    for reg_info in g_register_infos:
973        nibble_size = reg_info.bit_size() / 4
974        hex_value_str = packet.get_hex_chars(nibble_size)
975        if hex_value_str != None:
976            value = reg_info.get_value_from_hex_string (hex_value_str)
977            print '%*s = %s' % (g_max_register_info_name_len, reg_info.name(), value)
978        else:
979            return
980
981def cmd_read_all_regs(cmd, cmd_args):
982    packet = Packet(cmd_args)
983    packet.get_char() # toss the 'g' command character
984    tid = get_thread_from_thread_suffix (packet.str)
985    if tid != None:
986        print 'read_all_register(thread = 0x%4.4x)' % tid
987    else:
988        print 'read_all_register()'
989    return False
990
991def rsp_read_all_regs(options, cmd, cmd_args, rsp):
992    packet = Packet(rsp)
993    dump_all_regs (packet)
994
995def cmd_write_all_regs(options, cmd, args):
996    packet = Packet(args)
997    print 'write_all_registers()'
998    dump_all_regs (packet)
999    return False
1000
1001g_bp_types = [ "software_bp", "hardware_bp", "write_wp", "read_wp", "access_wp" ]
1002
1003def cmd_bp(options, cmd, args):
1004    if cmd == 'Z':
1005        s = 'set_'
1006    else:
1007        s = 'clear_'
1008    packet = Packet (args)
1009    bp_type = packet.get_hex_uint('big')
1010    packet.get_char() # Skip ,
1011    bp_addr = packet.get_hex_uint('big')
1012    packet.get_char() # Skip ,
1013    bp_size = packet.get_hex_uint('big')
1014    s += g_bp_types[bp_type]
1015    s += " (addr = 0x%x, size = %u)" % (bp_addr, bp_size)
1016    print s
1017    return False
1018
1019def cmd_mem_rgn_info(options, cmd, args):
1020    packet = Packet(args)
1021    packet.get_char() # skip ':' character
1022    addr = packet.get_hex_uint('big')
1023    print 'get_memory_region_info (addr=0x%x)' % (addr)
1024    return False
1025
1026def cmd_kill(options, cmd, args):
1027    print 'kill_process()'
1028    return False
1029
1030gdb_remote_commands = {
1031    '\\?'                     : { 'cmd' : cmd_stop_reply        , 'rsp' : rsp_stop_reply          , 'name' : "stop reply pacpket"},
1032    'qThreadStopInfo'         : { 'cmd' : cmd_qThreadStopInfo   , 'rsp' : rsp_stop_reply          , 'name' : "stop reply pacpket"},
1033    'QStartNoAckMode'         : { 'cmd' : cmd_query_packet      , 'rsp' : rsp_ok_means_supported  , 'name' : "query if no ack mode is supported"},
1034    'QThreadSuffixSupported'  : { 'cmd' : cmd_query_packet      , 'rsp' : rsp_ok_means_supported  , 'name' : "query if thread suffix is supported" },
1035    'QListThreadsInStopReply' : { 'cmd' : cmd_query_packet      , 'rsp' : rsp_ok_means_supported  , 'name' : "query if threads in stop reply packets are supported" },
1036    'QSetDetachOnError'       : { 'cmd' : cmd_query_packet      , 'rsp' : rsp_ok_means_success    , 'name' : "set if we should detach on error" },
1037    'QSetDisableASLR'         : { 'cmd' : cmd_query_packet      , 'rsp' : rsp_ok_means_success    , 'name' : "set if we should disable ASLR" },
1038    'qLaunchSuccess'          : { 'cmd' : cmd_query_packet      , 'rsp' : rsp_ok_means_success    , 'name' : "check on launch success for the A packet" },
1039    'A'                       : { 'cmd' : cmd_A                 , 'rsp' : rsp_ok_means_success    , 'name' : "launch process" },
1040    'QLaunchArch'             : { 'cmd' : cmd_query_packet      , 'rsp' : rsp_ok_means_supported  , 'name' : "set if we should disable ASLR" },
1041    'qVAttachOrWaitSupported' : { 'cmd' : cmd_query_packet      , 'rsp' : rsp_ok_means_supported  , 'name' : "set the launch architecture" },
1042    'qHostInfo'               : { 'cmd' : cmd_query_packet      , 'rsp' : rsp_dump_key_value_pairs, 'name' : "get host information" },
1043    'qC'                      : { 'cmd' : cmd_qC                , 'rsp' : rsp_qC                  , 'name' : "return the current thread ID" },
1044    'vCont'                   : { 'cmd' : cmd_vCont             , 'rsp' : rsp_vCont               , 'name' : "extended continue command" },
1045    'vAttach'                 : { 'cmd' : cmd_vAttach           , 'rsp' : rsp_stop_reply          , 'name' : "attach to process" },
1046    'c'                       : { 'cmd' : cmd_c                 , 'rsp' : rsp_stop_reply          , 'name' : "continue" },
1047    's'                       : { 'cmd' : cmd_s                 , 'rsp' : rsp_stop_reply          , 'name' : "step" },
1048    'qRegisterInfo'           : { 'cmd' : cmd_qRegisterInfo     , 'rsp' : rsp_qRegisterInfo       , 'name' : "query register info" },
1049    'qfThreadInfo'            : { 'cmd' : cmd_qThreadInfo       , 'rsp' : rsp_qThreadInfo         , 'name' : "get current thread list" },
1050    'qsThreadInfo'            : { 'cmd' : cmd_qThreadInfo       , 'rsp' : rsp_qThreadInfo         , 'name' : "get current thread list" },
1051    'qShlibInfoAddr'          : { 'cmd' : cmd_query_packet      , 'rsp' : rsp_hex_big_endian      , 'name' : "get shared library info address" },
1052    'qMemoryRegionInfo'       : { 'cmd' : cmd_mem_rgn_info      , 'rsp' : rsp_dump_key_value_pairs, 'name' : "get memory region information" },
1053    'qProcessInfo'            : { 'cmd' : cmd_query_packet      , 'rsp' : rsp_dump_key_value_pairs, 'name' : "get process info" },
1054    'qSupported'              : { 'cmd' : cmd_query_packet      , 'rsp' : rsp_ok_means_supported  , 'name' : "query supported" },
1055    'qXfer:'                  : { 'cmd' : cmd_qXfer             , 'rsp' : rsp_qXfer               , 'name' : "qXfer" },
1056    'qSymbol:'                : { 'cmd' : cmd_qSymbol           , 'rsp' : rsp_qSymbol             , 'name' : "qSymbol" },
1057    'x'                       : { 'cmd' : cmd_read_mem_bin      , 'rsp' : rsp_mem_bin_bytes       , 'name' : "read memory binary" },
1058    'X'                       : { 'cmd' : cmd_write_memory      , 'rsp' : rsp_ok_means_success    , 'name' : "write memory binary" },
1059    'm'                       : { 'cmd' : cmd_read_memory       , 'rsp' : rsp_memory_bytes        , 'name' : "read memory" },
1060    'M'                       : { 'cmd' : cmd_write_memory      , 'rsp' : rsp_ok_means_success    , 'name' : "write memory" },
1061    '_M'                      : { 'cmd' : cmd_alloc_memory      , 'rsp' : rsp_alloc_memory        , 'name' : "allocate memory" },
1062    '_m'                      : { 'cmd' : cmd_dealloc_memory    , 'rsp' : rsp_ok_means_success    , 'name' : "deallocate memory" },
1063    'p'                       : { 'cmd' : cmd_read_one_reg      , 'rsp' : rsp_read_one_reg        , 'name' : "read single register" },
1064    'P'                       : { 'cmd' : cmd_write_one_reg     , 'rsp' : rsp_ok_means_success    , 'name' : "write single register" },
1065    'g'                       : { 'cmd' : cmd_read_all_regs     , 'rsp' : rsp_read_all_regs       , 'name' : "read all registers" },
1066    'G'                       : { 'cmd' : cmd_write_all_regs    , 'rsp' : rsp_ok_means_success    , 'name' : "write all registers" },
1067    'z'                       : { 'cmd' : cmd_bp                , 'rsp' : rsp_ok_means_success    , 'name' : "clear breakpoint or watchpoint" },
1068    'Z'                       : { 'cmd' : cmd_bp                , 'rsp' : rsp_ok_means_success    , 'name' : "set breakpoint or watchpoint" },
1069    'k'                       : { 'cmd' : cmd_kill              , 'rsp' : rsp_stop_reply          , 'name' : "kill process" },
1070}
1071
1072def calculate_mean_and_standard_deviation(floats):
1073    sum = 0.0
1074    count = len(floats)
1075    if count == 0:
1076        return (0.0, 0.0)
1077    for f in floats:
1078        sum += f
1079    mean =  sum / count
1080    accum = 0.0
1081    for f in floats:
1082        delta = f - mean
1083        accum += delta * delta
1084
1085    std_dev = math.sqrt(accum / (count-1));
1086    return (mean, std_dev)
1087
1088def parse_gdb_log_file(path, options):
1089    f = open(path)
1090    parse_gdb_log(f, options)
1091    f.close()
1092
1093def parse_gdb_log(file, options):
1094    '''Parse a GDB log file that was generated by enabling logging with:
1095    (lldb) log enable --threadsafe --timestamp --file <FILE> gdb-remote packets
1096    This log file will contain timestamps and this function will then normalize
1097    those packets to be relative to the first value timestamp that is found and
1098    show delta times between log lines and also keep track of how long it takes
1099    for GDB remote commands to make a send/receive round trip. This can be
1100    handy when trying to figure out why some operation in the debugger is taking
1101    a long time during a preset set of debugger commands.'''
1102
1103    tricky_commands = [ 'qRegisterInfo' ]
1104    timestamp_regex = re.compile('(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$')
1105    packet_name_regex = re.compile('([A-Za-z_]+)[^a-z]')
1106    packet_transmit_name_regex = re.compile('(?P<direction>send|read) packet: (?P<packet>.*)')
1107    packet_contents_name_regex = re.compile('\$([^#]+)#[0-9a-fA-F]{2}')
1108    packet_names_regex_str = '(' + '|'.join(gdb_remote_commands.keys()) + ')(.*)';
1109    packet_names_regex = re.compile(packet_names_regex_str);
1110
1111    base_time = 0.0
1112    last_time = 0.0
1113    packet_send_time = 0.0
1114    packet_total_times = {}
1115    packet_times = []
1116    packet_count = {}
1117    lines = file.read().splitlines()
1118    last_command = None
1119    last_command_args = None
1120    last_command_packet = None
1121    hide_next_response = False
1122    for line in lines:
1123        m = packet_transmit_name_regex.search(line)
1124        is_command = False
1125        direction = None
1126        if m:
1127            direction = m.group('direction')
1128            is_command = direction == 'send'
1129            packet = m.group('packet')
1130            sys.stdout.write(options.colors.green())
1131            if not options.quiet and not hide_next_response:
1132                print '#  ', line
1133            sys.stdout.write(options.colors.reset())
1134
1135            #print 'direction = "%s", packet = "%s"' % (direction, packet)
1136
1137            if is_command:
1138                print '-->',
1139            else:
1140                print '<--',
1141            if packet[0] == '+':
1142                if not options.quiet: print 'ACK'
1143                continue
1144            elif packet[0] == '-':
1145                if not options.quiet: print 'NACK'
1146                continue
1147            elif packet[0] == '$':
1148                m = packet_contents_name_regex.match(packet)
1149                if m:
1150                    contents = m.group(1)
1151                    if is_command:
1152                        hide_next_response = False
1153                        m = packet_names_regex.match (contents)
1154                        if m:
1155                            last_command = m.group(1)
1156                            if last_command == '?':
1157                                last_command = '\\?'
1158                            packet_name = last_command
1159                            last_command_args = m.group(2)
1160                            last_command_packet = contents
1161                            hide_next_response = gdb_remote_commands[last_command]['cmd'](options, last_command, last_command_args)
1162                        else:
1163                            packet_match = packet_name_regex.match (contents)
1164                            if packet_match:
1165                                packet_name = packet_match.group(1)
1166                                for tricky_cmd in tricky_commands:
1167                                    if packet_name.find (tricky_cmd) == 0:
1168                                        packet_name = tricky_cmd
1169                            else:
1170                                packet_name = contents
1171                            last_command = None
1172                            last_command_args = None
1173                            last_command_packet = None
1174                    elif last_command:
1175                        gdb_remote_commands[last_command]['rsp'](options, last_command, last_command_args, contents)
1176                else:
1177                    print 'error: invalid packet: "', packet, '"'
1178            else:
1179                print '???'
1180        else:
1181            print '## ', line
1182        match = timestamp_regex.match (line)
1183        if match:
1184            curr_time = float (match.group(2))
1185            if last_time and not is_command:
1186                delta = curr_time - last_time
1187                packet_times.append(delta)
1188            delta = 0.0
1189            if base_time:
1190                delta = curr_time - last_time
1191            else:
1192                base_time = curr_time
1193
1194            if is_command:
1195                packet_send_time = curr_time
1196            elif line.find('read packet: $') >= 0 and packet_name:
1197                if packet_name in packet_total_times:
1198                    packet_total_times[packet_name] += delta
1199                    packet_count[packet_name] += 1
1200                else:
1201                    packet_total_times[packet_name] = delta
1202                    packet_count[packet_name] = 1
1203                packet_name = None
1204
1205            if not options or not options.quiet:
1206                print '%s%.6f %+.6f%s' % (match.group(1), curr_time - base_time, delta, match.group(3))
1207            last_time = curr_time
1208        # else:
1209        #     print line
1210    (average, std_dev) = calculate_mean_and_standard_deviation(packet_times)
1211    if average and std_dev:
1212        print '%u packets with average packet time of %f and standard deviation of %f' % (len(packet_times), average, std_dev)
1213    if packet_total_times:
1214        total_packet_time = 0.0
1215        total_packet_count = 0
1216        for key, vvv in packet_total_times.items():
1217            # print '  key = (%s) "%s"' % (type(key), key)
1218            # print 'value = (%s) %s' % (type(vvv), vvv)
1219            # if type(vvv) == 'float':
1220            total_packet_time += vvv
1221        for key, vvv in packet_count.items():
1222            total_packet_count += vvv
1223
1224        print '#---------------------------------------------------'
1225        print '# Packet timing summary:'
1226        print '# Totals: time = %6f, count = %6d' % (total_packet_time, total_packet_count)
1227        print '#---------------------------------------------------'
1228        print '# Packet                   Time (sec) Percent Count '
1229        print '#------------------------- ---------- ------- ------'
1230        if options and options.sort_count:
1231            res = sorted(packet_count, key=packet_count.__getitem__, reverse=True)
1232        else:
1233            res = sorted(packet_total_times, key=packet_total_times.__getitem__, reverse=True)
1234
1235        if last_time > 0.0:
1236            for item in res:
1237                packet_total_time = packet_total_times[item]
1238                packet_percent = (packet_total_time / total_packet_time)*100.0
1239                if packet_percent >= 10.0:
1240                    print "  %24s %.6f   %.2f%% %6d" % (item, packet_total_time, packet_percent, packet_count[item])
1241                else:
1242                    print "  %24s %.6f   %.2f%%  %6d" % (item, packet_total_time, packet_percent, packet_count[item])
1243
1244
1245
1246if __name__ == '__main__':
1247    usage = "usage: gdbremote [options]"
1248    description='''The command disassembles a GDB remote packet log.'''
1249    parser = optparse.OptionParser(description=description, prog='gdbremote',usage=usage)
1250    parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
1251    parser.add_option('-q', '--quiet', action='store_true', dest='quiet', help='display verbose debug info', default=False)
1252    parser.add_option('-C', '--color', action='store_true', dest='color', help='add terminal colors', default=False)
1253    parser.add_option('-c', '--sort-by-count', action='store_true', dest='sort_count', help='display verbose debug info', default=False)
1254    parser.add_option('--crashlog', type='string', dest='crashlog', help='symbolicate using a darwin crash log file', default=False)
1255    try:
1256        (options, args) = parser.parse_args(sys.argv[1:])
1257    except:
1258        print 'error: argument error'
1259        sys.exit(1)
1260
1261    options.colors = TerminalColors(options.color)
1262    options.symbolicator = None
1263    if options.crashlog:
1264        import lldb
1265        lldb.debugger = lldb.SBDebugger.Create()
1266        import lldb.macosx.crashlog
1267        options.symbolicator = lldb.macosx.crashlog.CrashLog(options.crashlog)
1268        print '%s' % (options.symbolicator)
1269
1270    # This script is being run from the command line, create a debugger in case we are
1271    # going to use any debugger functions in our function.
1272    if len(args):
1273        for file in args:
1274            print '#----------------------------------------------------------------------'
1275            print "# GDB remote log file: '%s'" % file
1276            print '#----------------------------------------------------------------------'
1277            parse_gdb_log_file (file, options)
1278        if options.symbolicator:
1279            print '%s' % (options.symbolicator)
1280    else:
1281        parse_gdb_log(sys.stdin, options)
1282
1283else:
1284    import lldb
1285    if lldb.debugger:
1286        # This initializer is being run from LLDB in the embedded command interpreter
1287        # Add any commands contained in this module to LLDB
1288        lldb.debugger.HandleCommand('command script add -f gdbremote.start_gdb_log start_gdb_log')
1289        lldb.debugger.HandleCommand('command script add -f gdbremote.stop_gdb_log stop_gdb_log')
1290        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'
1291