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