def test_ToolPath(self): for cpu_arch, binprefix in _EXPECTED_NDK_TOOL_SUBDIR_MAP.iteritems(): expected_binprefix = os.path.join(constants.ANDROID_NDK_ROOT, binprefix) expected_path = expected_binprefix + 'foo' self.assertEqual(host_paths.ToolPath('foo', cpu_arch), expected_path)
def DemangleSymbol(mangled_symbol): """Return the demangled form of mangled_symbol.""" cmd = [host_paths.ToolPath("c++filt", _arch)] process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) demangled_symbol, _ = process.communicate(mangled_symbol + '\n') return demangled_symbol
def _CallAddr2LineForSet(lib, unique_addrs, cpu_arch): """Look up line and symbol information for a set of addresses. Args: lib: library (or executable) pathname containing symbols unique_addrs: set of string hexidecimal addresses look up. cpu_arch: Target CPU architecture. Returns: A dictionary of the form {addr: [(symbol, file:line)]} where each address has a list of associated symbols and locations or an empty list if no symbol information was found. If the function has been inlined then the list may contain more than one element with the symbols for the most deeply nested inlined location appearing first. """ if not lib: return None symbols = SYMBOLS_DIR + lib if not os.path.splitext(symbols)[1] in ['', '.so', '.apk']: return None if not os.path.isfile(symbols): return None addrs = sorted(unique_addrs) result = {} def _Callback(sym, addr): records = [] while sym: # Traverse all the inlines following the |inlined_by| chain. if sym.source_path and sym.source_line: location = '%s:%d' % (sym.source_path, sym.source_line) else: location = None records += [(sym.name, location)] sym = sym.inlined_by result[addr] = records symbolizer = elf_symbolizer.ELFSymbolizer( elf_file_path=symbols, addr2line_path=host_paths.ToolPath("addr2line", cpu_arch), callback=_Callback, inlines=True) for addr in addrs: symbolizer.SymbolizeAsync(int(addr, 16), addr) symbolizer.Join() return result
def _CreateSymbolizerFor(self, host_path): """Create the ELFSymbolizer instance associated with a given lib path.""" addr2line_path = self._addr2line_path if not addr2line_path: if not self._android_abi: raise Exception( 'Android CPU ABI must be set before calling FindSymbolInfo!') cpu_arch = _AndroidAbiToCpuArch(self._android_abi) self._addr2line_path = host_paths.ToolPath('addr2line', cpu_arch) return elf_symbolizer.ELFSymbolizer( elf_file_path=host_path, addr2line_path=self._addr2line_path, callback=ElfSymbolResolver._Callback, inlines=True)
def SymbolInfosFromBinary(binary_filename): """Runs objdump to get all the symbols from a binary. Args: binary_filename: path to the binary. Returns: A list of SymbolInfo from the binary. """ command = (host_paths.ToolPath('objdump', _arch), '-t', '-w', binary_filename) p = subprocess.Popen(command, shell=False, stdout=subprocess.PIPE) try: result = _SymbolInfosFromStream(p.stdout) return result finally: p.stdout.close() p.wait()
def _CallObjdumpForSet(lib, unique_addrs, cpu_arch): """Use objdump to find out the names of the containing functions. Args: lib: library (or executable) pathname containing symbols unique_addrs: set of string hexidecimal addresses to find the functions for. cpu_arch: Target CPU architecture. Returns: A dictionary of the form {addr: (string symbol, offset)}. """ if not lib: return None symbols = SYMBOLS_DIR + lib if not os.path.exists(symbols): return None symbols = SYMBOLS_DIR + lib if not os.path.exists(symbols): return None result = {} # Function lines look like: # 000177b0 <android::IBinder::~IBinder()+0x2c>: # We pull out the address and function first. Then we check for an optional # offset. This is tricky due to functions that look like "operator+(..)+0x2c" func_regexp = re.compile("(^[a-f0-9]*) \<(.*)\>:$") offset_regexp = re.compile("(.*)\+0x([a-f0-9]*)") # A disassembly line looks like: # 177b2: b510 push {r4, lr} asm_regexp = re.compile("(^[ a-f0-9]*):[ a-f0-0]*.*$") for target_addr in unique_addrs: start_addr_dec = str(_StripPC(int(target_addr, 16), cpu_arch)) stop_addr_dec = str(_StripPC(int(target_addr, 16), cpu_arch) + 8) cmd = [host_paths.ToolPath("objdump", cpu_arch), "--section=.text", "--demangle", "--disassemble", "--start-address=" + start_addr_dec, "--stop-address=" + stop_addr_dec, symbols] current_symbol = None # The current function symbol in the disassembly. current_symbol_addr = 0 # The address of the current function. stream = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout for line in stream: # Is it a function line like: # 000177b0 <android::IBinder::~IBinder()>: components = func_regexp.match(line) if components: # This is a new function, so record the current function and its # address. current_symbol_addr = int(components.group(1), 16) current_symbol = components.group(2) # Does it have an optional offset like: "foo(..)+0x2c"? components = offset_regexp.match(current_symbol) if components: current_symbol = components.group(1) offset = components.group(2) if offset: current_symbol_addr -= int(offset, 16) # Is it an disassembly line like: # 177b2: b510 push {r4, lr} components = asm_regexp.match(line) if components: addr = components.group(1) i_addr = int(addr, 16) i_target = _StripPC(int(target_addr, 16), cpu_arch) if i_addr == i_target: result[target_addr] = (current_symbol, i_target - current_symbol_addr) stream.close() return result
def setUp(self): self._old_demangle = None if not os.path.exists(host_paths.ToolPath('c++filt', 'arm')): print 'Using fake demangling due to missing c++filt binary' self._old_demangle = symbol_extractor.DemangleSymbol symbol_extractor.DemangleSymbol = _FakeDemangle