1# Copyright 2017 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import os 7import re 8import subprocess 9import threading 10 11_CHROME_SRC = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir) 12_LLVM_SYMBOLIZER_PATH = os.path.join( 13 _CHROME_SRC, 'third_party', 'llvm-build', 'Release+Asserts', 'bin', 14 'llvm-symbolizer') 15 16_BINARY = re.compile(r'0b[0,1]+') 17_HEX = re.compile(r'0x[0-9,a-e]+') 18_OCTAL = re.compile(r'0[0-7]+') 19 20_UNKNOWN = '<UNKNOWN>' 21 22 23def _CheckValidAddr(addr): 24 """ 25 Check whether the addr is valid input to llvm symbolizer. 26 Valid addr has to be octal, binary, or hex number. 27 28 Args: 29 addr: addr to be entered to llvm symbolizer. 30 31 Returns: 32 whether the addr is valid input to llvm symbolizer. 33 """ 34 return _HEX.match(addr) or _OCTAL.match(addr) or _BINARY.match(addr) 35 36 37class LLVMSymbolizer(object): 38 def __init__(self): 39 """Create a LLVMSymbolizer instance that interacts with the llvm symbolizer. 40 41 The purpose of the LLVMSymbolizer is to get function names and line 42 numbers of an address from the symbols library. 43 """ 44 self._llvm_symbolizer_subprocess = None 45 # Allow only one thread to call GetSymbolInformation at a time. 46 self._lock = threading.Lock() 47 48 def Start(self): 49 """Start the llvm symbolizer subprocess. 50 51 Create a subprocess of the llvm symbolizer executable, which will be used 52 to retrieve function names etc. 53 """ 54 if os.path.isfile(_LLVM_SYMBOLIZER_PATH): 55 self._llvm_symbolizer_subprocess = subprocess.Popen( 56 [_LLVM_SYMBOLIZER_PATH], stdout=subprocess.PIPE, stdin=subprocess.PIPE) 57 else: 58 logging.error('Cannot find llvm_symbolizer here: %s.' % 59 _LLVM_SYMBOLIZER_PATH) 60 self._llvm_symbolizer_subprocess = None 61 62 def Close(self): 63 """Close the llvm symbolizer subprocess. 64 65 Close the subprocess by closing stdin, stdout and killing the subprocess. 66 """ 67 with self._lock: 68 if self._llvm_symbolizer_subprocess: 69 self._llvm_symbolizer_subprocess.kill() 70 self._llvm_symbolizer_subprocess = None 71 72 def __enter__(self): 73 """Start the llvm symbolizer subprocess.""" 74 self.Start() 75 return self 76 77 def __exit__(self, exc_type, exc_val, exc_tb): 78 """Close the llvm symbolizer subprocess.""" 79 self.Close() 80 81 def GetSymbolInformation(self, lib, addr): 82 """Return the corresponding function names and line numbers. 83 84 Args: 85 lib: library to search for info. 86 addr: address to look for info. 87 88 Returns: 89 A list of (function name, line numbers) tuple. 90 """ 91 if (self._llvm_symbolizer_subprocess is None or not lib 92 or not _CheckValidAddr(addr) or not os.path.isfile(lib)): 93 return [(_UNKNOWN, lib)] 94 95 with self._lock: 96 self._llvm_symbolizer_subprocess.stdin.write('%s %s\n' % (lib, addr)) 97 self._llvm_symbolizer_subprocess.stdin.flush() 98 99 result = [] 100 # Read till see new line, which is a symbol of end of output. 101 # One line of function name is always followed by one line of line number. 102 while True: 103 line = self._llvm_symbolizer_subprocess.stdout.readline() 104 if line != '\n': 105 line_numbers = self._llvm_symbolizer_subprocess.stdout.readline() 106 result.append( 107 (line[:-1], 108 line_numbers[:-1])) 109 else: 110 return result 111