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