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