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