def testIncompleteSyminfo(self): """Stimulate the symbol-not-resolved logic.""" symbolizer = elf_symbolizer.ELFSymbolizer( elf_file_path='/path/doesnt/matter/mock_lib1.so', addr2line_path=_MOCK_A2L_PATH, callback=self._callback, max_concurrent_jobs=1) # Test symbols with valid name but incomplete path. addr = _INCOMPLETE_MOCK_ADDR exp_name = 'mock_sym_for_addr_%d' % addr exp_source_path = None exp_source_line = None cb_arg = (addr, exp_name, exp_source_path, exp_source_line, False) symbolizer.SymbolizeAsync(addr, cb_arg) # Test symbols with no name or sym info. addr = _UNKNOWN_MOCK_ADDR exp_name = None exp_source_path = None exp_source_line = None cb_arg = (addr, exp_name, exp_source_path, exp_source_line, False) symbolizer.SymbolizeAsync(addr, cb_arg) symbolizer.Join()
def CallAddr2LineForSet(lib, unique_addrs): """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. 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 (label, platform, target) = FindToolchain() symbolizer = elf_symbolizer.ELFSymbolizer( elf_file_path=symbols, addr2line_path=ToolPath("addr2line"), 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 testInlines(self): """Stimulate the inline processing logic.""" symbolizer = elf_symbolizer.ELFSymbolizer( elf_file_path='/path/doesnt/matter/mock_lib1.so', addr2line_path=_MOCK_A2L_PATH, callback=self._callback, inlines=True, max_concurrent_jobs=4) for addr in xrange(1000): exp_inline = False exp_unknown = False # First 100 addresses with inlines. if addr < 100: addr += _INLINE_MOCK_ADDR exp_inline = True # Followed by 100 without inlines. elif addr < 200: pass # Followed by 100 interleaved inlines and not inlines. elif addr < 300: if addr & 1: addr += _INLINE_MOCK_ADDR exp_inline = True # Followed by 100 interleaved inlines and unknonwn. elif addr < 400: if addr & 1: addr += _INLINE_MOCK_ADDR exp_inline = True else: addr += _UNKNOWN_MOCK_ADDR exp_unknown = True exp_name = 'mock_sym_for_addr_%d' % addr if not exp_unknown else None exp_source_path = 'mock_src/mock_lib1.so.c' if not exp_unknown else None exp_source_line = addr if not exp_unknown else None cb_arg = (addr, exp_name, exp_source_path, exp_source_line, exp_inline) symbolizer.SymbolizeAsync(addr, cb_arg) symbolizer.Join()
def _RunTest(self, max_concurrent_jobs, num_symbols): symbolizer = elf_symbolizer.ELFSymbolizer( elf_file_path='/path/doesnt/matter/mock_lib1.so', addr2line_path=_MOCK_A2L_PATH, callback=self._callback, max_concurrent_jobs=max_concurrent_jobs, addr2line_timeout=0.5) for addr in xrange(num_symbols): exp_name = 'mock_sym_for_addr_%d' % addr exp_source_path = 'mock_src/mock_lib1.so.c' exp_source_line = addr cb_arg = (addr, exp_name, exp_source_path, exp_source_line, False) symbolizer.SymbolizeAsync(addr, cb_arg) symbolizer.Join() # Check that all the expected callbacks have been received. for addr in xrange(num_symbols): self.assertIn(addr, self._resolved_addresses) self._resolved_addresses.remove(addr) # Check for unexpected callbacks. self.assertEqual(len(self._resolved_addresses), 0)
def ExtractSymbols(self, native_heaps, sym_paths): """Performs symbolization. Returns a |symbol.Symbols| from |NativeHeap|s. This method performs the symbolization but does NOT decorate (i.e. add symbol/source info) to the stack frames of |native_heaps|. The heaps can be decorated as needed using the native_heap.SymbolizeUsingSymbolDB() method. Rationale: the most common use case in this application is: symbolize-and-store-symbols and load-symbols-and-decorate-heaps (in two different stages at two different times). Args: native_heaps: a collection of native_heap.NativeHeap instances. sym_paths: either a list of or a string of comma-separated symbol paths. """ assert (all( isinstance(x, native_heap.NativeHeap) for x in native_heaps)) symbols = symbol.Symbols() # Find addr2line in toolchain_path. if isinstance(sym_paths, basestring): sym_paths = sym_paths.split(',') matches = glob.glob( os.path.join(self.settings['toolchain_path'], '*addr2line')) if not matches: raise exceptions.MemoryInspectorException('Cannot find addr2line') addr2line_path = matches[0] # First group all the stack frames together by lib path. frames_by_lib = {} for nheap in native_heaps: for stack_frame in nheap.stack_frames.itervalues(): frames = frames_by_lib.setdefault( stack_frame.exec_file_rel_path, set()) frames.add(stack_frame) # The symbolization process is asynchronous (but yet single-threaded). This # callback is invoked every time the symbol info for a stack frame is ready. def SymbolizeAsyncCallback(sym_info, stack_frame): if not sym_info.name: return sym = symbol.Symbol(name=sym_info.name, source_file_path=sym_info.source_path, line_number=sym_info.source_line) symbols.Add(stack_frame.exec_file_rel_path, stack_frame.offset, sym) # TODO(primiano): support inline sym info (i.e. |sym_info.inlined_by|). # Perform the actual symbolization (ordered by lib). for exec_file_rel_path, frames in frames_by_lib.iteritems(): # Look up the full path of the symbol in the sym paths. exec_file_name = posixpath.basename(exec_file_rel_path) if exec_file_rel_path.startswith('/'): exec_file_rel_path = exec_file_rel_path[1:] exec_file_abs_path = '' for sym_path in sym_paths: # First try to locate the symbol file following the full relative path # e.g. /host/syms/ + /system/lib/foo.so => /host/syms/system/lib/foo.so. exec_file_abs_path = os.path.join(sym_path, exec_file_rel_path) if os.path.exists(exec_file_abs_path): break # If no luck, try looking just for the file name in the sym path, # e.g. /host/syms/ + (/system/lib/)foo.so => /host/syms/foo.so exec_file_abs_path = os.path.join(sym_path, exec_file_name) if os.path.exists(exec_file_abs_path): break if not os.path.exists(exec_file_abs_path): continue symbolizer = elf_symbolizer.ELFSymbolizer( elf_file_path=exec_file_abs_path, addr2line_path=addr2line_path, callback=SymbolizeAsyncCallback, inlines=False) # Kick off the symbolizer and then wait that all callbacks are issued. for stack_frame in sorted(frames, key=lambda x: x.offset): symbolizer.SymbolizeAsync(stack_frame.offset, stack_frame) symbolizer.Join() return symbols