diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/examples/python/gdbremote.py | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/examples/python/gdbremote.py')
-rwxr-xr-x | gnu/llvm/lldb/examples/python/gdbremote.py | 1630 |
1 files changed, 1630 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/examples/python/gdbremote.py b/gnu/llvm/lldb/examples/python/gdbremote.py new file mode 100755 index 00000000000..dcacbfccdaf --- /dev/null +++ b/gnu/llvm/lldb/examples/python/gdbremote.py @@ -0,0 +1,1630 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# This module will enable GDB remote packet logging when the +# 'start_gdb_log' command is called with a filename to log to. When the +# 'stop_gdb_log' command is called, it will disable the logging and +# print out statistics about how long commands took to execute and also +# will primnt ou +# Be sure to add the python path that points to the LLDB shared library. +# +# To use this in the embedded python interpreter using "lldb" just +# import it with the full path using the "command script import" +# command. This can be done from the LLDB command line: +# (lldb) command script import /path/to/gdbremote.py +# Or it can be added to your ~/.lldbinit file so this module is always +# available. +#---------------------------------------------------------------------- + +import binascii +import subprocess +import json +import math +import optparse +import os +import re +import shlex +import string +import sys +import tempfile +import xml.etree.ElementTree as ET + +#---------------------------------------------------------------------- +# Global variables +#---------------------------------------------------------------------- +g_log_file = '' +g_byte_order = 'little' +g_number_regex = re.compile('^(0x[0-9a-fA-F]+|[0-9]+)') +g_thread_id_regex = re.compile('^(-1|[0-9a-fA-F]+|0)') + + +class TerminalColors: + '''Simple terminal colors class''' + + def __init__(self, enabled=True): + # TODO: discover terminal type from "file" and disable if + # it can't handle the color codes + self.enabled = enabled + + def reset(self): + '''Reset all terminal colors and formatting.''' + if self.enabled: + return "\x1b[0m" + return '' + + def bold(self, on=True): + '''Enable or disable bold depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[1m" + else: + return "\x1b[22m" + return '' + + def italics(self, on=True): + '''Enable or disable italics depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[3m" + else: + return "\x1b[23m" + return '' + + def underline(self, on=True): + '''Enable or disable underline depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[4m" + else: + return "\x1b[24m" + return '' + + def inverse(self, on=True): + '''Enable or disable inverse depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[7m" + else: + return "\x1b[27m" + return '' + + def strike(self, on=True): + '''Enable or disable strike through depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[9m" + else: + return "\x1b[29m" + return '' + + def black(self, fg=True): + '''Set the foreground or background color to black. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[30m" + else: + return "\x1b[40m" + return '' + + def red(self, fg=True): + '''Set the foreground or background color to red. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[31m" + else: + return "\x1b[41m" + return '' + + def green(self, fg=True): + '''Set the foreground or background color to green. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[32m" + else: + return "\x1b[42m" + return '' + + def yellow(self, fg=True): + '''Set the foreground or background color to yellow. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[33m" + else: + return "\x1b[43m" + return '' + + def blue(self, fg=True): + '''Set the foreground or background color to blue. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[34m" + else: + return "\x1b[44m" + return '' + + def magenta(self, fg=True): + '''Set the foreground or background color to magenta. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[35m" + else: + return "\x1b[45m" + return '' + + def cyan(self, fg=True): + '''Set the foreground or background color to cyan. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[36m" + else: + return "\x1b[46m" + return '' + + def white(self, fg=True): + '''Set the foreground or background color to white. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[37m" + else: + return "\x1b[47m" + return '' + + def default(self, fg=True): + '''Set the foreground or background color to the default. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[39m" + else: + return "\x1b[49m" + return '' + + +def start_gdb_log(debugger, command, result, dict): + '''Start logging GDB remote packets by enabling logging with timestamps and + thread safe logging. Follow a call to this function with a call to "stop_gdb_log" + in order to dump out the commands.''' + global g_log_file + command_args = shlex.split(command) + usage = "usage: start_gdb_log [options] [<LOGFILEPATH>]" + 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 + be aggregated and displayed.''' + parser = optparse.OptionParser( + description=description, + prog='start_gdb_log', + usage=usage) + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + help='display verbose debug info', + default=False) + try: + (options, args) = parser.parse_args(command_args) + except: + return + + if g_log_file: + result.PutCString( + 'error: logging is already in progress with file "%s"' % + g_log_file) + else: + args_len = len(args) + if args_len == 0: + g_log_file = tempfile.mktemp() + elif len(args) == 1: + g_log_file = args[0] + + if g_log_file: + debugger.HandleCommand( + 'log enable --threadsafe --timestamp --file "%s" gdb-remote packets' % + g_log_file) + 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) + return + + result.PutCString('error: invalid log file path') + result.PutCString(usage) + + +def stop_gdb_log(debugger, command, result, dict): + '''Stop logging GDB remote packets to the file that was specified in a call + to "start_gdb_log" and normalize the timestamps to be relative to the first + timestamp in the log file. Also print out statistics for how long each + command took to allow performance bottlenecks to be determined.''' + global g_log_file + # Any commands whose names might be followed by more valid C identifier + # characters must be listed here + command_args = shlex.split(command) + usage = "usage: stop_gdb_log [options]" + 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.''' + parser = optparse.OptionParser( + description=description, + prog='stop_gdb_log', + usage=usage) + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + help='display verbose debug info', + default=False) + parser.add_option( + '-q', + '--quiet', + action='store_true', + dest='quiet', + help='display verbose debug info', + default=False) + parser.add_option( + '-C', + '--color', + action='store_true', + dest='color', + help='add terminal colors', + default=False) + parser.add_option( + '-c', + '--sort-by-count', + action='store_true', + dest='sort_count', + help='display verbose debug info', + default=False) + parser.add_option( + '-s', + '--symbolicate', + action='store_true', + dest='symbolicate', + help='symbolicate addresses in log using current "lldb.target"', + default=False) + try: + (options, args) = parser.parse_args(command_args) + except: + return + options.colors = TerminalColors(options.color) + options.symbolicator = None + if options.symbolicate: + if lldb.target: + import lldb.utils.symbolication + options.symbolicator = lldb.utils.symbolication.Symbolicator() + options.symbolicator.target = lldb.target + else: + print("error: can't symbolicate without a target") + + if not g_log_file: + result.PutCString( + 'error: logging must have been previously enabled with a call to "stop_gdb_log"') + elif os.path.exists(g_log_file): + if len(args) == 0: + debugger.HandleCommand('log disable gdb-remote packets') + result.PutCString( + "GDB packet logging disabled. Logged packets are in '%s'" % + g_log_file) + parse_gdb_log_file(g_log_file, options) + else: + result.PutCString(usage) + else: + print('error: the GDB packet log file "%s" does not exist' % g_log_file) + + +def is_hex_byte(str): + if len(str) == 2: + return str[0] in string.hexdigits and str[1] in string.hexdigits + return False + +def get_hex_string_if_all_printable(str): + try: + s = binascii.unhexlify(str) + if all(c in string.printable for c in s): + return s + except TypeError: + pass + return None + +# global register info list +g_register_infos = list() +g_max_register_info_name_len = 0 + + +class RegisterInfo: + """Class that represents register information""" + + def __init__(self, kvp): + self.info = dict() + for kv in kvp: + key = kv[0] + value = kv[1] + self.info[key] = value + + def name(self): + '''Get the name of the register.''' + if self.info and 'name' in self.info: + return self.info['name'] + return None + + def bit_size(self): + '''Get the size in bits of the register.''' + if self.info and 'bitsize' in self.info: + return int(self.info['bitsize']) + return 0 + + def byte_size(self): + '''Get the size in bytes of the register.''' + return self.bit_size() / 8 + + def get_value_from_hex_string(self, hex_str): + '''Dump the register value given a native byte order encoded hex ASCII byte string.''' + encoding = self.info['encoding'] + bit_size = self.bit_size() + packet = Packet(hex_str) + if encoding == 'uint': + uval = packet.get_hex_uint(g_byte_order) + if bit_size == 8: + return '0x%2.2x' % (uval) + elif bit_size == 16: + return '0x%4.4x' % (uval) + elif bit_size == 32: + return '0x%8.8x' % (uval) + elif bit_size == 64: + return '0x%16.16x' % (uval) + bytes = list() + uval = packet.get_hex_uint8() + while uval is not None: + bytes.append(uval) + uval = packet.get_hex_uint8() + value_str = '0x' + if g_byte_order == 'little': + bytes.reverse() + for byte in bytes: + value_str += '%2.2x' % byte + return '%s' % (value_str) + + def __str__(self): + '''Dump the register info key/value pairs''' + s = '' + for key in self.info.keys(): + if s: + s += ', ' + s += "%s=%s " % (key, self.info[key]) + return s + + +class Packet: + """Class that represents a packet that contains string data""" + + def __init__(self, packet_str): + self.str = packet_str + + def peek_char(self): + ch = 0 + if self.str: + ch = self.str[0] + return ch + + def get_char(self): + ch = 0 + if self.str: + ch = self.str[0] + self.str = self.str[1:] + return ch + + def skip_exact_string(self, s): + if self.str and self.str.startswith(s): + self.str = self.str[len(s):] + return True + else: + return False + + def get_thread_id(self, fail_value=-1): + match = g_number_regex.match(self.str) + if match: + number_str = match.group(1) + self.str = self.str[len(number_str):] + return int(number_str, 0) + else: + return fail_value + + def get_hex_uint8(self): + if self.str and len(self.str) >= 2 and self.str[ + 0] in string.hexdigits and self.str[1] in string.hexdigits: + uval = int(self.str[0:2], 16) + self.str = self.str[2:] + return uval + return None + + def get_hex_uint16(self, byte_order): + uval = 0 + if byte_order == 'big': + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() + else: + uval |= self.get_hex_uint8() + uval |= self.get_hex_uint8() << 8 + return uval + + def get_hex_uint32(self, byte_order): + uval = 0 + if byte_order == 'big': + uval |= self.get_hex_uint8() << 24 + uval |= self.get_hex_uint8() << 16 + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() + else: + uval |= self.get_hex_uint8() + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() << 16 + uval |= self.get_hex_uint8() << 24 + return uval + + def get_hex_uint64(self, byte_order): + uval = 0 + if byte_order == 'big': + uval |= self.get_hex_uint8() << 56 + uval |= self.get_hex_uint8() << 48 + uval |= self.get_hex_uint8() << 40 + uval |= self.get_hex_uint8() << 32 + uval |= self.get_hex_uint8() << 24 + uval |= self.get_hex_uint8() << 16 + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() + else: + uval |= self.get_hex_uint8() + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() << 16 + uval |= self.get_hex_uint8() << 24 + uval |= self.get_hex_uint8() << 32 + uval |= self.get_hex_uint8() << 40 + uval |= self.get_hex_uint8() << 48 + uval |= self.get_hex_uint8() << 56 + return uval + + def get_number(self, fail_value=-1): + '''Get a number from the packet. The number must be in big endian format and should be parsed + according to its prefix (starts with "0x" means hex, starts with "0" means octal, starts with + [1-9] means decimal, etc)''' + match = g_number_regex.match(self.str) + if match: + number_str = match.group(1) + self.str = self.str[len(number_str):] + return int(number_str, 0) + else: + return fail_value + + def get_hex_ascii_str(self, n=0): + hex_chars = self.get_hex_chars(n) + if hex_chars: + return binascii.unhexlify(hex_chars) + else: + return None + + def get_hex_chars(self, n=0): + str_len = len(self.str) + if n == 0: + # n was zero, so we need to determine all hex chars and + # stop when we hit the end of the string of a non-hex character + while n < str_len and self.str[n] in string.hexdigits: + n = n + 1 + else: + if n > str_len: + return None # Not enough chars + # Verify all chars are hex if a length was specified + for i in range(n): + if self.str[i] not in string.hexdigits: + return None # Not all hex digits + if n == 0: + return None + hex_str = self.str[0:n] + self.str = self.str[n:] + return hex_str + + def get_hex_uint(self, byte_order, n=0): + if byte_order == 'big': + hex_str = self.get_hex_chars(n) + if hex_str is None: + return None + return int(hex_str, 16) + else: + uval = self.get_hex_uint8() + if uval is None: + return None + uval_result = 0 + shift = 0 + while uval is not None: + uval_result |= (uval << shift) + shift += 8 + uval = self.get_hex_uint8() + return uval_result + + def get_key_value_pairs(self): + kvp = list() + if ';' in self.str: + key_value_pairs = string.split(self.str, ';') + for key_value_pair in key_value_pairs: + if len(key_value_pair): + kvp.append(string.split(key_value_pair, ':')) + return kvp + + def split(self, ch): + return string.split(self.str, ch) + + def split_hex(self, ch, byte_order): + hex_values = list() + strings = string.split(self.str, ch) + for str in strings: + hex_values.append(Packet(str).get_hex_uint(byte_order)) + return hex_values + + def __str__(self): + return self.str + + def __len__(self): + return len(self.str) + +g_thread_suffix_regex = re.compile(';thread:([0-9a-fA-F]+);') + + +def get_thread_from_thread_suffix(str): + if str: + match = g_thread_suffix_regex.match(str) + if match: + return int(match.group(1), 16) + return None + + +def cmd_qThreadStopInfo(options, cmd, args): + packet = Packet(args) + tid = packet.get_hex_uint('big') + print("get_thread_stop_info (tid = 0x%x)" % (tid)) + + +def cmd_stop_reply(options, cmd, args): + print("get_last_stop_info()") + return False + + +def rsp_stop_reply(options, cmd, cmd_args, rsp): + global g_byte_order + packet = Packet(rsp) + stop_type = packet.get_char() + if stop_type == 'T' or stop_type == 'S': + signo = packet.get_hex_uint8() + key_value_pairs = packet.get_key_value_pairs() + for key_value_pair in key_value_pairs: + key = key_value_pair[0] + if is_hex_byte(key): + reg_num = Packet(key).get_hex_uint8() + if reg_num < len(g_register_infos): + reg_info = g_register_infos[reg_num] + key_value_pair[0] = reg_info.name() + key_value_pair[1] = reg_info.get_value_from_hex_string( + key_value_pair[1]) + elif key == 'jthreads' or key == 'jstopinfo': + key_value_pair[1] = binascii.unhexlify(key_value_pair[1]) + key_value_pairs.insert(0, ['signal', signo]) + print('stop_reply():') + dump_key_value_pairs(key_value_pairs) + elif stop_type == 'W': + exit_status = packet.get_hex_uint8() + print('stop_reply(): exit (status=%i)' % exit_status) + elif stop_type == 'O': + print('stop_reply(): stdout = "%s"' % packet.str) + + +def cmd_unknown_packet(options, cmd, args): + if args: + print("cmd: %s, args: %s", cmd, args) + else: + print("cmd: %s", cmd) + return False + + +def cmd_qSymbol(options, cmd, args): + if args == ':': + print('ready to serve symbols') + else: + packet = Packet(args) + symbol_addr = packet.get_hex_uint('big') + if symbol_addr is None: + if packet.skip_exact_string(':'): + symbol_name = packet.get_hex_ascii_str() + print('lookup_symbol("%s") -> symbol not available yet' % (symbol_name)) + else: + print('error: bad command format') + else: + if packet.skip_exact_string(':'): + symbol_name = packet.get_hex_ascii_str() + print('lookup_symbol("%s") -> 0x%x' % (symbol_name, symbol_addr)) + else: + print('error: bad command format') + +def cmd_QSetWithHexString(options, cmd, args): + print('%s("%s")' % (cmd[:-1], binascii.unhexlify(args))) + +def cmd_QSetWithString(options, cmd, args): + print('%s("%s")' % (cmd[:-1], args)) + +def cmd_QSetWithUnsigned(options, cmd, args): + print('%s(%i)' % (cmd[:-1], int(args))) + +def rsp_qSymbol(options, cmd, cmd_args, rsp): + if len(rsp) == 0: + print("Unsupported") + else: + if rsp == "OK": + print("No more symbols to lookup") + else: + packet = Packet(rsp) + if packet.skip_exact_string("qSymbol:"): + symbol_name = packet.get_hex_ascii_str() + print('lookup_symbol("%s")' % (symbol_name)) + else: + print('error: response string should start with "qSymbol:": respnse is "%s"' % (rsp)) + + +def cmd_qXfer(options, cmd, args): + # $qXfer:features:read:target.xml:0,1ffff#14 + print("read target special data %s" % (args)) + return True + + +def rsp_qXfer(options, cmd, cmd_args, rsp): + data = string.split(cmd_args, ':') + if data[0] == 'features': + if data[1] == 'read': + filename, extension = os.path.splitext(data[2]) + if extension == '.xml': + response = Packet(rsp) + xml_string = response.get_hex_ascii_str() + if xml_string: + ch = xml_string[0] + if ch == 'l': + xml_string = xml_string[1:] + xml_root = ET.fromstring(xml_string) + for reg_element in xml_root.findall("./feature/reg"): + if not 'value_regnums' in reg_element.attrib: + reg_info = RegisterInfo([]) + if 'name' in reg_element.attrib: + reg_info.info[ + 'name'] = reg_element.attrib['name'] + else: + reg_info.info['name'] = 'unspecified' + if 'encoding' in reg_element.attrib: + reg_info.info['encoding'] = reg_element.attrib[ + 'encoding'] + else: + reg_info.info['encoding'] = 'uint' + if 'offset' in reg_element.attrib: + reg_info.info[ + 'offset'] = reg_element.attrib['offset'] + if 'bitsize' in reg_element.attrib: + reg_info.info[ + 'bitsize'] = reg_element.attrib['bitsize'] + g_register_infos.append(reg_info) + print('XML for "%s":' % (data[2])) + ET.dump(xml_root) + + +def cmd_A(options, cmd, args): + print('launch process:') + packet = Packet(args) + while True: + arg_len = packet.get_number() + if arg_len == -1: + break + if not packet.skip_exact_string(','): + break + arg_idx = packet.get_number() + if arg_idx == -1: + break + if not packet.skip_exact_string(','): + break + arg_value = packet.get_hex_ascii_str(arg_len) + print('argv[%u] = "%s"' % (arg_idx, arg_value)) + + +def cmd_qC(options, cmd, args): + print("query_current_thread_id()") + + +def rsp_qC(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + if packet.skip_exact_string("QC"): + tid = packet.get_thread_id() + print("current_thread_id = %#x" % (tid)) + else: + print("current_thread_id = old thread ID") + + +def cmd_query_packet(options, cmd, args): + if args: + print("%s%s" % (cmd, args)) + else: + print("%s" % (cmd)) + return False + + +def rsp_ok_error(rsp): + print("rsp: ", rsp) + + +def rsp_ok_means_supported(options, cmd, cmd_args, rsp): + if rsp == 'OK': + print("%s%s is supported" % (cmd, cmd_args)) + elif rsp == '': + print("%s%s is not supported" % (cmd, cmd_args)) + else: + print("%s%s -> %s" % (cmd, cmd_args, rsp)) + + +def rsp_ok_means_success(options, cmd, cmd_args, rsp): + if rsp == 'OK': + print("success") + elif rsp == '': + print("%s%s is not supported" % (cmd, cmd_args)) + else: + print("%s%s -> %s" % (cmd, cmd_args, rsp)) + + +def dump_key_value_pairs(key_value_pairs): + max_key_len = 0 + for key_value_pair in key_value_pairs: + key_len = len(key_value_pair[0]) + if max_key_len < key_len: + max_key_len = key_len + for key_value_pair in key_value_pairs: + key = key_value_pair[0] + value = key_value_pair[1] + unhex_value = get_hex_string_if_all_printable(value) + if unhex_value: + print("%*s = %s (%s)" % (max_key_len, key, value, unhex_value)) + else: + print("%*s = %s" % (max_key_len, key, value)) + + +def rsp_dump_key_value_pairs(options, cmd, cmd_args, rsp): + if rsp: + print('%s response:' % (cmd)) + packet = Packet(rsp) + key_value_pairs = packet.get_key_value_pairs() + dump_key_value_pairs(key_value_pairs) + else: + print("not supported") + + +def cmd_c(options, cmd, args): + print("continue()") + return False + + +def cmd_s(options, cmd, args): + print("step()") + return False + + +def cmd_qSpeedTest(options, cmd, args): + print(("qSpeedTest: cmd='%s', args='%s'" % (cmd, args))) + + +def rsp_qSpeedTest(options, cmd, cmd_args, rsp): + print(("qSpeedTest: rsp='%s' cmd='%s', args='%s'" % (rsp, cmd, args))) + + +def cmd_vCont(options, cmd, args): + if args == '?': + print("%s: get supported extended continue modes" % (cmd)) + else: + got_other_threads = 0 + s = '' + for thread_action in string.split(args[1:], ';'): + (short_action, thread) = string.split(thread_action, ':') + tid = int(thread, 16) + if short_action == 'c': + action = 'continue' + elif short_action == 's': + action = 'step' + elif short_action[0] == 'C': + action = 'continue with signal 0x%s' % (short_action[1:]) + elif short_action == 'S': + action = 'step with signal 0x%s' % (short_action[1:]) + else: + action = short_action + if s: + s += ', ' + if tid == -1: + got_other_threads = 1 + s += 'other-threads:' + else: + s += 'thread 0x%4.4x: %s' % (tid, action) + if got_other_threads: + print("extended_continue (%s)" % (s)) + else: + print("extended_continue (%s, other-threads: suspend)" % (s)) + return False + + +def rsp_vCont(options, cmd, cmd_args, rsp): + if cmd_args == '?': + # Skip the leading 'vCont;' + rsp = rsp[6:] + modes = string.split(rsp, ';') + s = "%s: supported extended continue modes include: " % (cmd) + + for i, mode in enumerate(modes): + if i: + s += ', ' + if mode == 'c': + s += 'continue' + elif mode == 'C': + s += 'continue with signal' + elif mode == 's': + s += 'step' + elif mode == 'S': + s += 'step with signal' + elif mode == 't': + s += 'stop' + # else: + # s += 'unrecognized vCont mode: ', str(mode) + print(s) + elif rsp: + if rsp[0] == 'T' or rsp[0] == 'S' or rsp[0] == 'W' or rsp[0] == 'X': + rsp_stop_reply(options, cmd, cmd_args, rsp) + return + if rsp[0] == 'O': + print("stdout: %s" % (rsp)) + return + else: + print("not supported (cmd = '%s', args = '%s', rsp = '%s')" % (cmd, cmd_args, rsp)) + + +def cmd_vAttach(options, cmd, args): + (extra_command, args) = string.split(args, ';') + if extra_command: + print("%s%s(%s)" % (cmd, extra_command, args)) + else: + print("attach(pid = %u)" % int(args, 16)) + return False + + +def cmd_qRegisterInfo(options, cmd, args): + print('query_register_info(reg_num=%i)' % (int(args, 16))) + return False + + +def rsp_qRegisterInfo(options, cmd, cmd_args, rsp): + global g_max_register_info_name_len + print('query_register_info(reg_num=%i):' % (int(cmd_args, 16)), end=' ') + if len(rsp) == 3 and rsp[0] == 'E': + g_max_register_info_name_len = 0 + for reg_info in g_register_infos: + name_len = len(reg_info.name()) + if g_max_register_info_name_len < name_len: + g_max_register_info_name_len = name_len + print(' DONE') + else: + packet = Packet(rsp) + reg_info = RegisterInfo(packet.get_key_value_pairs()) + g_register_infos.append(reg_info) + print(reg_info) + return False + + +def cmd_qThreadInfo(options, cmd, args): + if cmd == 'qfThreadInfo': + query_type = 'first' + else: + query_type = 'subsequent' + print('get_current_thread_list(type=%s)' % (query_type)) + return False + + +def rsp_qThreadInfo(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + response_type = packet.get_char() + if response_type == 'm': + tids = packet.split_hex(';', 'big') + for i, tid in enumerate(tids): + if i: + print(',', end=' ') + print('0x%x' % (tid), end=' ') + print() + elif response_type == 'l': + print('END') + + +def rsp_hex_big_endian(options, cmd, cmd_args, rsp): + if rsp == '': + print("%s%s is not supported" % (cmd, cmd_args)) + else: + packet = Packet(rsp) + uval = packet.get_hex_uint('big') + print('%s: 0x%x' % (cmd, uval)) + + +def cmd_read_mem_bin(options, cmd, args): + # x0x7fff5fc39200,0x200 + packet = Packet(args) + addr = packet.get_hex_uint('big') + comma = packet.get_char() + size = packet.get_hex_uint('big') + print('binary_read_memory (addr = 0x%16.16x, size = %u)' % (addr, size)) + return False + + +def rsp_mem_bin_bytes(options, cmd, cmd_args, rsp): + packet = Packet(cmd_args) + addr = packet.get_hex_uint('big') + comma = packet.get_char() + size = packet.get_hex_uint('big') + print('memory:') + if size > 0: + dump_hex_memory_buffer(addr, rsp) + + +def cmd_read_memory(options, cmd, args): + packet = Packet(args) + addr = packet.get_hex_uint('big') + comma = packet.get_char() + size = packet.get_hex_uint('big') + print('read_memory (addr = 0x%16.16x, size = %u)' % (addr, size)) + return False + + +def dump_hex_memory_buffer(addr, hex_byte_str): + packet = Packet(hex_byte_str) + idx = 0 + ascii = '' + uval = packet.get_hex_uint8() + while uval is not None: + if ((idx % 16) == 0): + if ascii: + print(' ', ascii) + ascii = '' + print('0x%x:' % (addr + idx), end=' ') + print('%2.2x' % (uval), end=' ') + if 0x20 <= uval and uval < 0x7f: + ascii += '%c' % uval + else: + ascii += '.' + uval = packet.get_hex_uint8() + idx = idx + 1 + if ascii: + print(' ', ascii) + ascii = '' + + +def cmd_write_memory(options, cmd, args): + packet = Packet(args) + addr = packet.get_hex_uint('big') + if packet.get_char() != ',': + print('error: invalid write memory command (missing comma after address)') + return + size = packet.get_hex_uint('big') + if packet.get_char() != ':': + print('error: invalid write memory command (missing colon after size)') + return + print('write_memory (addr = 0x%16.16x, size = %u, data:' % (addr, size)) + dump_hex_memory_buffer(addr, packet.str) + return False + + +def cmd_alloc_memory(options, cmd, args): + packet = Packet(args) + byte_size = packet.get_hex_uint('big') + if packet.get_char() != ',': + print('error: invalid allocate memory command (missing comma after address)') + return + print('allocate_memory (byte-size = %u (0x%x), permissions = %s)' % (byte_size, byte_size, packet.str)) + return False + + +def rsp_alloc_memory(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + addr = packet.get_hex_uint('big') + print('addr = 0x%x' % addr) + + +def cmd_dealloc_memory(options, cmd, args): + packet = Packet(args) + addr = packet.get_hex_uint('big') + if packet.get_char() != ',': + print('error: invalid allocate memory command (missing comma after address)') + else: + print('deallocate_memory (addr = 0x%x, permissions = %s)' % (addr, packet.str)) + return False + + +def rsp_memory_bytes(options, cmd, cmd_args, rsp): + addr = Packet(cmd_args).get_hex_uint('big') + dump_hex_memory_buffer(addr, rsp) + + +def get_register_name_equal_value(options, reg_num, hex_value_str): + if reg_num < len(g_register_infos): + reg_info = g_register_infos[reg_num] + value_str = reg_info.get_value_from_hex_string(hex_value_str) + s = reg_info.name() + ' = ' + if options.symbolicator: + symbolicated_addresses = options.symbolicator.symbolicate( + int(value_str, 0)) + if symbolicated_addresses: + s += options.colors.magenta() + s += '%s' % symbolicated_addresses[0] + s += options.colors.reset() + return s + s += value_str + return s + else: + reg_value = Packet(hex_value_str).get_hex_uint(g_byte_order) + return 'reg(%u) = 0x%x' % (reg_num, reg_value) + + +def cmd_read_one_reg(options, cmd, args): + packet = Packet(args) + reg_num = packet.get_hex_uint('big') + tid = get_thread_from_thread_suffix(packet.str) + name = None + if reg_num < len(g_register_infos): + name = g_register_infos[reg_num].name() + if packet.str: + packet.get_char() # skip ; + thread_info = packet.get_key_value_pairs() + tid = int(thread_info[0][1], 16) + s = 'read_register (reg_num=%u' % reg_num + if name: + s += ' (%s)' % (name) + if tid is not None: + s += ', tid = 0x%4.4x' % (tid) + s += ')' + print(s) + return False + + +def rsp_read_one_reg(options, cmd, cmd_args, rsp): + packet = Packet(cmd_args) + reg_num = packet.get_hex_uint('big') + print(get_register_name_equal_value(options, reg_num, rsp)) + + +def cmd_write_one_reg(options, cmd, args): + packet = Packet(args) + reg_num = packet.get_hex_uint('big') + if packet.get_char() != '=': + print('error: invalid register write packet') + else: + name = None + hex_value_str = packet.get_hex_chars() + tid = get_thread_from_thread_suffix(packet.str) + s = 'write_register (reg_num=%u' % reg_num + if name: + s += ' (%s)' % (name) + s += ', value = ' + s += get_register_name_equal_value(options, reg_num, hex_value_str) + if tid is not None: + s += ', tid = 0x%4.4x' % (tid) + s += ')' + print(s) + return False + + +def dump_all_regs(packet): + for reg_info in g_register_infos: + nibble_size = reg_info.bit_size() / 4 + hex_value_str = packet.get_hex_chars(nibble_size) + if hex_value_str is not None: + value = reg_info.get_value_from_hex_string(hex_value_str) + print('%*s = %s' % (g_max_register_info_name_len, reg_info.name(), value)) + else: + return + + +def cmd_read_all_regs(cmd, cmd_args): + packet = Packet(cmd_args) + packet.get_char() # toss the 'g' command character + tid = get_thread_from_thread_suffix(packet.str) + if tid is not None: + print('read_all_register(thread = 0x%4.4x)' % tid) + else: + print('read_all_register()') + return False + + +def rsp_read_all_regs(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + dump_all_regs(packet) + + +def cmd_write_all_regs(options, cmd, args): + packet = Packet(args) + print('write_all_registers()') + dump_all_regs(packet) + return False + +g_bp_types = ["software_bp", "hardware_bp", "write_wp", "read_wp", "access_wp"] + + +def cmd_bp(options, cmd, args): + if cmd == 'Z': + s = 'set_' + else: + s = 'clear_' + packet = Packet(args) + bp_type = packet.get_hex_uint('big') + packet.get_char() # Skip , + bp_addr = packet.get_hex_uint('big') + packet.get_char() # Skip , + bp_size = packet.get_hex_uint('big') + s += g_bp_types[bp_type] + s += " (addr = 0x%x, size = %u)" % (bp_addr, bp_size) + print(s) + return False + + +def cmd_mem_rgn_info(options, cmd, args): + packet = Packet(args) + packet.get_char() # skip ':' character + addr = packet.get_hex_uint('big') + print('get_memory_region_info (addr=0x%x)' % (addr)) + return False + + +def cmd_kill(options, cmd, args): + print('kill_process()') + return False + + +def cmd_jThreadsInfo(options, cmd, args): + print('jThreadsInfo()') + return False + + +def cmd_jGetLoadedDynamicLibrariesInfos(options, cmd, args): + print('jGetLoadedDynamicLibrariesInfos()') + return False + + +def decode_packet(s, start_index=0): + # print '\ndecode_packet("%s")' % (s[start_index:]) + index = s.find('}', start_index) + have_escapes = index != -1 + if have_escapes: + normal_s = s[start_index:index] + else: + normal_s = s[start_index:] + # print 'normal_s = "%s"' % (normal_s) + if have_escapes: + escape_char = '%c' % (ord(s[index + 1]) ^ 0x20) + # print 'escape_char for "%s" = %c' % (s[index:index+2], escape_char) + return normal_s + escape_char + decode_packet(s, index + 2) + else: + return normal_s + + +def rsp_json(options, cmd, cmd_args, rsp): + print('%s() reply:' % (cmd)) + json_tree = json.loads(rsp) + print(json.dumps(json_tree, indent=4, separators=(',', ': '))) + + +def rsp_jGetLoadedDynamicLibrariesInfos(options, cmd, cmd_args, rsp): + if cmd_args: + rsp_json(options, cmd, cmd_args, rsp) + else: + rsp_ok_means_supported(options, cmd, cmd_args, rsp) + +gdb_remote_commands = { + '\\?': {'cmd': cmd_stop_reply, 'rsp': rsp_stop_reply, 'name': "stop reply pacpket"}, + 'qThreadStopInfo': {'cmd': cmd_qThreadStopInfo, 'rsp': rsp_stop_reply, 'name': "stop reply pacpket"}, + 'QStartNoAckMode': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query if no ack mode is supported"}, + 'QThreadSuffixSupported': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query if thread suffix is supported"}, + 'QListThreadsInStopReply': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query if threads in stop reply packets are supported"}, + 'QSetDetachOnError:': {'cmd': cmd_QSetWithUnsigned, 'rsp': rsp_ok_means_success, 'name': "set if we should detach on error"}, + 'QSetDisableASLR:': {'cmd': cmd_QSetWithUnsigned, 'rsp': rsp_ok_means_success, 'name': "set if we should disable ASLR"}, + 'qLaunchSuccess': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_success, 'name': "check on launch success for the A packet"}, + 'A': {'cmd': cmd_A, 'rsp': rsp_ok_means_success, 'name': "launch process"}, + 'QLaunchArch:': {'cmd': cmd_QSetWithString, 'rsp': rsp_ok_means_supported, 'name': "set the arch to launch in case the file contains multiple architectures"}, + 'qVAttachOrWaitSupported': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "set the launch architecture"}, + 'qHostInfo': {'cmd': cmd_query_packet, 'rsp': rsp_dump_key_value_pairs, 'name': "get host information"}, + 'qC': {'cmd': cmd_qC, 'rsp': rsp_qC, 'name': "return the current thread ID"}, + 'vCont': {'cmd': cmd_vCont, 'rsp': rsp_vCont, 'name': "extended continue command"}, + 'qSpeedTest': {'cmd':cmd_qSpeedTest, 'rsp': rsp_qSpeedTest, 'name': 'speed test packdet'}, + 'vAttach': {'cmd': cmd_vAttach, 'rsp': rsp_stop_reply, 'name': "attach to process"}, + 'c': {'cmd': cmd_c, 'rsp': rsp_stop_reply, 'name': "continue"}, + 's': {'cmd': cmd_s, 'rsp': rsp_stop_reply, 'name': "step"}, + 'qRegisterInfo': {'cmd': cmd_qRegisterInfo, 'rsp': rsp_qRegisterInfo, 'name': "query register info"}, + 'qfThreadInfo': {'cmd': cmd_qThreadInfo, 'rsp': rsp_qThreadInfo, 'name': "get current thread list"}, + 'qsThreadInfo': {'cmd': cmd_qThreadInfo, 'rsp': rsp_qThreadInfo, 'name': "get current thread list"}, + 'qShlibInfoAddr': {'cmd': cmd_query_packet, 'rsp': rsp_hex_big_endian, 'name': "get shared library info address"}, + 'qMemoryRegionInfo': {'cmd': cmd_mem_rgn_info, 'rsp': rsp_dump_key_value_pairs, 'name': "get memory region information"}, + 'qProcessInfo': {'cmd': cmd_query_packet, 'rsp': rsp_dump_key_value_pairs, 'name': "get process info"}, + 'qSupported': {'cmd': cmd_query_packet, 'rsp': rsp_ok_means_supported, 'name': "query supported"}, + 'qXfer:': {'cmd': cmd_qXfer, 'rsp': rsp_qXfer, 'name': "qXfer"}, + 'qSymbol:': {'cmd': cmd_qSymbol, 'rsp': rsp_qSymbol, 'name': "qSymbol"}, + 'QSetSTDIN:' : {'cmd' : cmd_QSetWithHexString, 'rsp' : rsp_ok_means_success, 'name': "set STDIN prior to launching with A packet"}, + 'QSetSTDOUT:' : {'cmd' : cmd_QSetWithHexString, 'rsp' : rsp_ok_means_success, 'name': "set STDOUT prior to launching with A packet"}, + 'QSetSTDERR:' : {'cmd' : cmd_QSetWithHexString, 'rsp' : rsp_ok_means_success, 'name': "set STDERR prior to launching with A packet"}, + 'QEnvironment:' : {'cmd' : cmd_QSetWithString, 'rsp' : rsp_ok_means_success, 'name': "set an environment variable prior to launching with A packet"}, + 'QEnvironmentHexEncoded:' : {'cmd' : cmd_QSetWithHexString, 'rsp' : rsp_ok_means_success, 'name': "set an environment variable prior to launching with A packet"}, + 'x': {'cmd': cmd_read_mem_bin, 'rsp': rsp_mem_bin_bytes, 'name': "read memory binary"}, + 'X': {'cmd': cmd_write_memory, 'rsp': rsp_ok_means_success, 'name': "write memory binary"}, + 'm': {'cmd': cmd_read_memory, 'rsp': rsp_memory_bytes, 'name': "read memory"}, + 'M': {'cmd': cmd_write_memory, 'rsp': rsp_ok_means_success, 'name': "write memory"}, + '_M': {'cmd': cmd_alloc_memory, 'rsp': rsp_alloc_memory, 'name': "allocate memory"}, + '_m': {'cmd': cmd_dealloc_memory, 'rsp': rsp_ok_means_success, 'name': "deallocate memory"}, + 'p': {'cmd': cmd_read_one_reg, 'rsp': rsp_read_one_reg, 'name': "read single register"}, + 'P': {'cmd': cmd_write_one_reg, 'rsp': rsp_ok_means_success, 'name': "write single register"}, + 'g': {'cmd': cmd_read_all_regs, 'rsp': rsp_read_all_regs, 'name': "read all registers"}, + 'G': {'cmd': cmd_write_all_regs, 'rsp': rsp_ok_means_success, 'name': "write all registers"}, + 'z': {'cmd': cmd_bp, 'rsp': rsp_ok_means_success, 'name': "clear breakpoint or watchpoint"}, + 'Z': {'cmd': cmd_bp, 'rsp': rsp_ok_means_success, 'name': "set breakpoint or watchpoint"}, + 'k': {'cmd': cmd_kill, 'rsp': rsp_stop_reply, 'name': "kill process"}, + 'jThreadsInfo': {'cmd': cmd_jThreadsInfo, 'rsp': rsp_json, 'name': "JSON get all threads info"}, + 'jGetLoadedDynamicLibrariesInfos:': {'cmd': cmd_jGetLoadedDynamicLibrariesInfos, 'rsp': rsp_jGetLoadedDynamicLibrariesInfos, 'name': 'JSON get loaded dynamic libraries'}, +} + + +def calculate_mean_and_standard_deviation(floats): + sum = 0.0 + count = len(floats) + if count == 0: + return (0.0, 0.0) + for f in floats: + sum += f + mean = sum / count + accum = 0.0 + for f in floats: + delta = f - mean + accum += delta * delta + + std_dev = math.sqrt(accum / (count - 1)) + return (mean, std_dev) + + +def parse_gdb_log_file(path, options): + f = open(path) + parse_gdb_log(f, options) + f.close() + + +def round_up(n, incr): + return float(((int(n) + incr) / incr) * incr) + + +def plot_latencies(sec_times): + # import numpy as np + import matplotlib.pyplot as plt + + for (i, name) in enumerate(sec_times.keys()): + times = sec_times[name] + if len(times) <= 1: + continue + plt.subplot(2, 1, 1) + plt.title('Packet "%s" Times' % (name)) + plt.xlabel('Packet') + units = 'ms' + adj_times = [] + max_time = 0.0 + for time in times: + time = time * 1000.0 + adj_times.append(time) + if time > max_time: + max_time = time + if max_time < 1.0: + units = 'us' + max_time = 0.0 + for i in range(len(adj_times)): + adj_times[i] *= 1000.0 + if adj_times[i] > max_time: + max_time = adj_times[i] + plt.ylabel('Time (%s)' % (units)) + max_y = None + for i in [5.0, 10.0, 25.0, 50.0]: + if max_time < i: + max_y = round_up(max_time, i) + break + if max_y is None: + max_y = round_up(max_time, 100.0) + plt.ylim(0.0, max_y) + plt.plot(adj_times, 'o-') + plt.show() + + +def parse_gdb_log(file, options): + '''Parse a GDB log file that was generated by enabling logging with: + (lldb) log enable --threadsafe --timestamp --file <FILE> gdb-remote packets + This log file will contain timestamps and this function will then normalize + those packets to be relative to the first value timestamp that is found and + show delta times between log lines and also keep track of how long it takes + for GDB remote commands to make a send/receive round trip. This can be + handy when trying to figure out why some operation in the debugger is taking + a long time during a preset set of debugger commands.''' + + tricky_commands = ['qRegisterInfo'] + timestamp_regex = re.compile('(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$') + packet_name_regex = re.compile('([A-Za-z_]+)[^a-z]') + packet_transmit_name_regex = re.compile( + '(?P<direction>send|read) packet: (?P<packet>.*)') + packet_contents_name_regex = re.compile('\$([^#]*)#[0-9a-fA-F]{2}') + packet_checksum_regex = re.compile('.*#[0-9a-fA-F]{2}$') + packet_names_regex_str = '(' + \ + '|'.join(gdb_remote_commands.keys()) + ')(.*)' + packet_names_regex = re.compile(packet_names_regex_str) + + base_time = 0.0 + last_time = 0.0 + min_time = 100000000.0 + packet_total_times = {} + all_packet_times = [] + packet_times = {} + packet_counts = {} + lines = file.read().splitlines() + last_command = None + last_command_args = None + last_command_packet = None + hide_next_response = False + num_lines = len(lines) + skip_count = 0 + for (line_index, line) in enumerate(lines): + # See if we need to skip any lines + if skip_count > 0: + skip_count -= 1 + continue + m = packet_transmit_name_regex.search(line) + is_command = False + direction = None + if m: + direction = m.group('direction') + is_command = direction == 'send' + packet = m.group('packet') + sys.stdout.write(options.colors.green()) + if not options.quiet and not hide_next_response: + print('# ', line) + sys.stdout.write(options.colors.reset()) + + # print 'direction = "%s", packet = "%s"' % (direction, packet) + + if packet[0] == '+': + if is_command: + print('-->', end=' ') + else: + print('<--', end=' ') + if not options.quiet: + print('ACK') + continue + elif packet[0] == '-': + if is_command: + print('-->', end=' ') + else: + print('<--', end=' ') + if not options.quiet: + print('NACK') + continue + elif packet[0] == '$': + m = packet_contents_name_regex.match(packet) + if not m and packet[0] == '$': + multiline_packet = packet + idx = line_index + 1 + while idx < num_lines: + if not options.quiet and not hide_next_response: + print('# ', lines[idx]) + multiline_packet += lines[idx] + m = packet_contents_name_regex.match(multiline_packet) + if m: + packet = multiline_packet + skip_count = idx - line_index + break + else: + idx += 1 + if m: + if is_command: + print('-->', end=' ') + else: + print('<--', end=' ') + contents = decode_packet(m.group(1)) + if is_command: + hide_next_response = False + m = packet_names_regex.match(contents) + if m: + last_command = m.group(1) + if last_command == '?': + last_command = '\\?' + packet_name = last_command + last_command_args = m.group(2) + last_command_packet = contents + hide_next_response = gdb_remote_commands[last_command][ + 'cmd'](options, last_command, last_command_args) + else: + packet_match = packet_name_regex.match(contents) + if packet_match: + packet_name = packet_match.group(1) + for tricky_cmd in tricky_commands: + if packet_name.find(tricky_cmd) == 0: + packet_name = tricky_cmd + else: + packet_name = contents + last_command = None + last_command_args = None + last_command_packet = None + elif last_command: + gdb_remote_commands[last_command]['rsp']( + options, last_command, last_command_args, contents) + else: + print('error: invalid packet: "', packet, '"') + else: + print('???') + else: + print('## ', line) + match = timestamp_regex.match(line) + if match: + curr_time = float(match.group(2)) + if last_time and not is_command: + delta = curr_time - last_time + all_packet_times.append(delta) + delta = 0.0 + if base_time: + delta = curr_time - last_time + else: + base_time = curr_time + + if not is_command: + if line.find('read packet: $') >= 0 and packet_name: + if packet_name in packet_total_times: + packet_total_times[packet_name] += delta + packet_counts[packet_name] += 1 + else: + packet_total_times[packet_name] = delta + packet_counts[packet_name] = 1 + if packet_name not in packet_times: + packet_times[packet_name] = [] + packet_times[packet_name].append(delta) + packet_name = None + if min_time > delta: + min_time = delta + + if not options or not options.quiet: + print('%s%.6f %+.6f%s' % (match.group(1), + curr_time - base_time, + delta, + match.group(3))) + last_time = curr_time + # else: + # print line + (average, std_dev) = calculate_mean_and_standard_deviation(all_packet_times) + if average and std_dev: + print('%u packets with average packet time of %f and standard deviation of %f' % (len(all_packet_times), average, std_dev)) + if packet_total_times: + total_packet_time = 0.0 + total_packet_count = 0 + for key, vvv in packet_total_times.items(): + # print ' key = (%s) "%s"' % (type(key), key) + # print 'value = (%s) %s' % (type(vvv), vvv) + # if type(vvv) == 'float': + total_packet_time += vvv + for key, vvv in packet_counts.items(): + total_packet_count += vvv + + print('#------------------------------------------------------------') + print('# Packet timing summary:') + print('# Totals: time = %6f, count = %6d' % (total_packet_time, + total_packet_count)) + print('# Min packet time: time = %6f' % (min_time)) + print('#------------------------------------------------------------') + print('# Packet Time (sec) Percent Count Latency') + print('#------------------------- ----------- ------- ------ -------') + if options and options.sort_count: + res = sorted( + packet_counts, + key=packet_counts.__getitem__, + reverse=True) + else: + res = sorted( + packet_total_times, + key=packet_total_times.__getitem__, + reverse=True) + + if last_time > 0.0: + for item in res: + packet_total_time = packet_total_times[item] + packet_percent = ( + packet_total_time / total_packet_time) * 100.0 + packet_count = packet_counts[item] + print(" %24s %11.6f %5.2f%% %6d %9.6f" % ( + item, packet_total_time, packet_percent, packet_count, + float(packet_total_time) / float(packet_count))) + if options.plot: + plot_latencies(packet_times) + +if __name__ == '__main__': + usage = "usage: gdbremote [options]" + description = '''The command disassembles a GDB remote packet log.''' + parser = optparse.OptionParser( + description=description, + prog='gdbremote', + usage=usage) + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + help='display verbose debug info', + default=False) + parser.add_option( + '--plot', + action='store_true', + dest='plot', + help='plot packet latencies by packet type', + default=False) + parser.add_option( + '-q', + '--quiet', + action='store_true', + dest='quiet', + help='display verbose debug info', + default=False) + parser.add_option( + '-C', + '--color', + action='store_true', + dest='color', + help='add terminal colors', + default=False) + parser.add_option( + '-c', + '--sort-by-count', + action='store_true', + dest='sort_count', + help='display verbose debug info', + default=False) + parser.add_option( + '--crashlog', + type='string', + dest='crashlog', + help='symbolicate using a darwin crash log file', + default=False) + try: + (options, args) = parser.parse_args(sys.argv[1:]) + except: + print('error: argument error') + sys.exit(1) + + options.colors = TerminalColors(options.color) + options.symbolicator = None + if options.crashlog: + import lldb + lldb.debugger = lldb.SBDebugger.Create() + import lldb.macosx.crashlog + options.symbolicator = lldb.macosx.crashlog.CrashLog(options.crashlog) + print('%s' % (options.symbolicator)) + + # This script is being run from the command line, create a debugger in case we are + # going to use any debugger functions in our function. + if len(args): + for file in args: + print('#----------------------------------------------------------------------') + print("# GDB remote log file: '%s'" % file) + print('#----------------------------------------------------------------------') + parse_gdb_log_file(file, options) + if options.symbolicator: + print('%s' % (options.symbolicator)) + else: + parse_gdb_log(sys.stdin, options) + +else: + import lldb + if lldb.debugger: + # This initializer is being run from LLDB in the embedded command interpreter + # Add any commands contained in this module to LLDB + lldb.debugger.HandleCommand( + 'command script add -f gdbremote.start_gdb_log start_gdb_log') + lldb.debugger.HandleCommand( + 'command script add -f gdbremote.stop_gdb_log stop_gdb_log') + 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') |