| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 #===- lib/asan/scripts/asan_symbolize.py -----------------------------------===# | 2 #===- lib/asan/scripts/asan_symbolize.py -----------------------------------===# |
| 3 # | 3 # |
| 4 # The LLVM Compiler Infrastructure | 4 # The LLVM Compiler Infrastructure |
| 5 # | 5 # |
| 6 # This file is distributed under the University of Illinois Open Source | 6 # This file is distributed under the University of Illinois Open Source |
| 7 # License. See LICENSE.TXT for details. | 7 # License. See LICENSE.TXT for details. |
| 8 # | 8 # |
| 9 #===------------------------------------------------------------------------===# | 9 #===------------------------------------------------------------------------===# |
| 10 import argparse | 10 import argparse |
| 11 import bisect | 11 import bisect |
| 12 import getopt | 12 import getopt |
| 13 import os | 13 import os |
| 14 import pty | |
| 15 import re | 14 import re |
| 16 import subprocess | 15 import subprocess |
| 17 import sys | 16 import sys |
| 18 import termios | |
| 19 | 17 |
| 20 symbolizers = {} | 18 symbolizers = {} |
| 21 DEBUG = False | 19 DEBUG = False |
| 22 demangle = False | 20 demangle = False |
| 23 binutils_prefix = None | 21 binutils_prefix = None |
| 24 sysroot_path = None | 22 sysroot_path = None |
| 25 binary_name_filter = None | 23 binary_name_filter = None |
| 26 fix_filename_patterns = None | 24 fix_filename_patterns = None |
| 27 logfile = sys.stdin | 25 logfile = sys.stdin |
| 28 | 26 |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 164 file_name = fix_filename(file_name) | 162 file_name = fix_filename(file_name) |
| 165 return ['%s in %s %s' % (addr, function_name, file_name)] | 163 return ['%s in %s %s' % (addr, function_name, file_name)] |
| 166 | 164 |
| 167 | 165 |
| 168 class UnbufferedLineConverter(object): | 166 class UnbufferedLineConverter(object): |
| 169 """ | 167 """ |
| 170 Wrap a child process that responds to each line of input with one line of | 168 Wrap a child process that responds to each line of input with one line of |
| 171 output. Uses pty to trick the child into providing unbuffered output. | 169 output. Uses pty to trick the child into providing unbuffered output. |
| 172 """ | 170 """ |
| 173 def __init__(self, args, close_stderr=False): | 171 def __init__(self, args, close_stderr=False): |
| 172 # Local imports so that the script can start on Windows. |
| 173 import pty |
| 174 import termios |
| 174 pid, fd = pty.fork() | 175 pid, fd = pty.fork() |
| 175 if pid == 0: | 176 if pid == 0: |
| 176 # We're the child. Transfer control to command. | 177 # We're the child. Transfer control to command. |
| 177 if close_stderr: | 178 if close_stderr: |
| 178 dev_null = os.open('/dev/null', 0) | 179 dev_null = os.open('/dev/null', 0) |
| 179 os.dup2(dev_null, 2) | 180 os.dup2(dev_null, 2) |
| 180 os.execvp(args[0], args) | 181 os.execvp(args[0], args) |
| 181 else: | 182 else: |
| 182 # Disable echoing. | 183 # Disable echoing. |
| 183 attr = termios.tcgetattr(fd) | 184 attr = termios.tcgetattr(fd) |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 334 result = ['%s in %s %s:%d' % ( | 335 result = ['%s in %s %s:%d' % ( |
| 335 addr, function_name, file_name, line_no)] | 336 addr, function_name, file_name, line_no)] |
| 336 print result | 337 print result |
| 337 return result | 338 return result |
| 338 else: | 339 else: |
| 339 return None | 340 return None |
| 340 | 341 |
| 341 | 342 |
| 342 class SymbolizationLoop(object): | 343 class SymbolizationLoop(object): |
| 343 def __init__(self, binary_name_filter=None, dsym_hint_producer=None): | 344 def __init__(self, binary_name_filter=None, dsym_hint_producer=None): |
| 344 # Used by clients who may want to supply a different binary name. | 345 if sys.platform == 'win32': |
| 345 # E.g. in Chrome several binaries may share a single .dSYM. | 346 # ASan on Windows uses dbghelp.dll to symbolize in-process, which works |
| 346 self.binary_name_filter = binary_name_filter | 347 # even in sandboxed processes. Nothing needs to be done here. |
| 347 self.dsym_hint_producer = dsym_hint_producer | 348 self.process_line = self.process_line_echo |
| 348 self.system = os.uname()[0] | 349 else: |
| 349 if self.system not in ['Linux', 'Darwin', 'FreeBSD']: | 350 # Used by clients who may want to supply a different binary name. |
| 350 raise Exception('Unknown system') | 351 # E.g. in Chrome several binaries may share a single .dSYM. |
| 351 self.llvm_symbolizers = {} | 352 self.binary_name_filter = binary_name_filter |
| 352 self.last_llvm_symbolizer = None | 353 self.dsym_hint_producer = dsym_hint_producer |
| 353 self.dsym_hints = set([]) | 354 self.system = os.uname()[0] |
| 354 self.frame_no = 0 | 355 if self.system not in ['Linux', 'Darwin', 'FreeBSD']: |
| 356 raise Exception('Unknown system') |
| 357 self.llvm_symbolizers = {} |
| 358 self.last_llvm_symbolizer = None |
| 359 self.dsym_hints = set([]) |
| 360 self.frame_no = 0 |
| 361 self.process_line = self.process_line_posix |
| 355 | 362 |
| 356 def symbolize_address(self, addr, binary, offset): | 363 def symbolize_address(self, addr, binary, offset): |
| 357 # On non-Darwin (i.e. on platforms without .dSYM debug info) always use | 364 # On non-Darwin (i.e. on platforms without .dSYM debug info) always use |
| 358 # a single symbolizer binary. | 365 # a single symbolizer binary. |
| 359 # On Darwin, if the dsym hint producer is present: | 366 # On Darwin, if the dsym hint producer is present: |
| 360 # 1. check whether we've seen this binary already; if so, | 367 # 1. check whether we've seen this binary already; if so, |
| 361 # use |llvm_symbolizers[binary]|, which has already loaded the debug | 368 # use |llvm_symbolizers[binary]|, which has already loaded the debug |
| 362 # info for this binary (might not be the case for | 369 # info for this binary (might not be the case for |
| 363 # |last_llvm_symbolizer|); | 370 # |last_llvm_symbolizer|); |
| 364 # 2. otherwise check if we've seen all the hints for this binary already; | 371 # 2. otherwise check if we've seen all the hints for this binary already; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 398 return [self.current_line] | 405 return [self.current_line] |
| 399 else: | 406 else: |
| 400 result = [] | 407 result = [] |
| 401 for symbolized_frame in symbolized_lines: | 408 for symbolized_frame in symbolized_lines: |
| 402 result.append(' #%s %s' % (str(self.frame_no), symbolized_frame.rstri
p())) | 409 result.append(' #%s %s' % (str(self.frame_no), symbolized_frame.rstri
p())) |
| 403 self.frame_no += 1 | 410 self.frame_no += 1 |
| 404 return result | 411 return result |
| 405 | 412 |
| 406 def process_logfile(self): | 413 def process_logfile(self): |
| 407 self.frame_no = 0 | 414 self.frame_no = 0 |
| 408 while True: | 415 for line in logfile: |
| 409 line = logfile.readline() | |
| 410 if not line: | |
| 411 break | |
| 412 processed = self.process_line(line) | 416 processed = self.process_line(line) |
| 413 print '\n'.join(processed) | 417 print '\n'.join(processed) |
| 414 | 418 |
| 415 def process_line(self, line): | 419 def process_line_echo(self, line): |
| 420 return [line.rstrip()] |
| 421 |
| 422 def process_line_posix(self, line): |
| 416 self.current_line = line.rstrip() | 423 self.current_line = line.rstrip() |
| 417 #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45) | 424 #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45) |
| 418 stack_trace_line_format = ( | 425 stack_trace_line_format = ( |
| 419 '^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)') | 426 '^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)') |
| 420 match = re.match(stack_trace_line_format, line) | 427 match = re.match(stack_trace_line_format, line) |
| 421 if not match: | 428 if not match: |
| 422 return [self.current_line] | 429 return [self.current_line] |
| 423 if DEBUG: | 430 if DEBUG: |
| 424 print line | 431 print line |
| 425 _, frameno_str, addr, binary, offset = match.groups() | 432 _, frameno_str, addr, binary, offset = match.groups() |
| 426 if frameno_str == '0': | 433 if frameno_str == '0': |
| 427 # Assume that frame #0 is the first frame of new stack trace. | 434 # Assume that frame #0 is the first frame of new stack trace. |
| 428 self.frame_no = 0 | 435 self.frame_no = 0 |
| 429 original_binary = binary | 436 original_binary = binary |
| 430 if self.binary_name_filter: | 437 if self.binary_name_filter: |
| 431 binary = self.binary_name_filter(binary) | 438 binary = self.binary_name_filter(binary) |
| 432 symbolized_line = self.symbolize_address(addr, binary, offset) | 439 symbolized_line = self.symbolize_address(addr, binary, offset) |
| 433 if not symbolized_line: | 440 if not symbolized_line: |
| 434 if original_binary != binary: | 441 if original_binary != binary: |
| 435 symbolized_line = self.symbolize_address(addr, binary, offset) | 442 symbolized_line = self.symbolize_address(addr, binary, offset) |
| 436 return self.get_symbolized_lines(symbolized_line) | 443 return self.get_symbolized_lines(symbolized_line) |
| 437 | 444 |
| 438 | 445 |
| 439 if __name__ == '__main__': | 446 if __name__ == '__main__': |
| 440 parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFo
rmatter, | 447 parser = argparse.ArgumentParser( |
| 441 description='ASan symbolization script', | 448 formatter_class=argparse.RawDescriptionHelpFormatter, |
| 442 epilog='''Example of use: | 449 description='ASan symbolization script', |
| 443 asan_symbolize.py -c "$HOME/opt/cross/bin/arm-linux-gnueabi-" -s "$HOME/Symbol
Files" < asan.log''') | 450 epilog='Example of use:\n' |
| 451 'asan_symbolize.py -c "$HOME/opt/cross/bin/arm-linux-gnueabi-" ' |
| 452 '-s "$HOME/SymbolFiles" < asan.log') |
| 444 parser.add_argument('path_to_cut', nargs='*', | 453 parser.add_argument('path_to_cut', nargs='*', |
| 445 help='pattern to be cut from the result file path ') | 454 help='pattern to be cut from the result file path ') |
| 446 parser.add_argument('-d','--demangle', action='store_true', | 455 parser.add_argument('-d','--demangle', action='store_true', |
| 447 help='demangle function names') | 456 help='demangle function names') |
| 448 parser.add_argument('-s', metavar='SYSROOT', | 457 parser.add_argument('-s', metavar='SYSROOT', |
| 449 help='set path to sysroot for sanitized binaries') | 458 help='set path to sysroot for sanitized binaries') |
| 450 parser.add_argument('-c', metavar='CROSS_COMPILE', | 459 parser.add_argument('-c', metavar='CROSS_COMPILE', |
| 451 help='set prefix for binutils') | 460 help='set prefix for binutils') |
| 452 parser.add_argument('-l','--logfile', default=sys.stdin, type=argparse.FileTyp
e('r'), | 461 parser.add_argument('-l','--logfile', default=sys.stdin, |
| 453 help='set log file name to parse, default is stdin') | 462 type=argparse.FileType('r'), |
| 463 help='set log file name to parse, default is stdin') |
| 454 args = parser.parse_args() | 464 args = parser.parse_args() |
| 455 if args.path_to_cut: | 465 if args.path_to_cut: |
| 456 fix_filename_patterns = args.path_to_cut | 466 fix_filename_patterns = args.path_to_cut |
| 457 if args.demangle: | 467 if args.demangle: |
| 458 demangle = True | 468 demangle = True |
| 459 if args.s: | 469 if args.s: |
| 460 binary_name_filter = sysroot_path_filter | 470 binary_name_filter = sysroot_path_filter |
| 461 sysroot_path = args.s | 471 sysroot_path = args.s |
| 462 if args.c: | 472 if args.c: |
| 463 binutils_prefix = args.c | 473 binutils_prefix = args.c |
| 464 if args.logfile: | 474 if args.logfile: |
| 465 logfile = args.logfile | 475 logfile = args.logfile |
| 466 else: | 476 else: |
| 467 logfile = sys.stdin | 477 logfile = sys.stdin |
| 468 loop = SymbolizationLoop(binary_name_filter) | 478 loop = SymbolizationLoop(binary_name_filter) |
| 469 loop.process_logfile() | 479 loop.process_logfile() |
| OLD | NEW |