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