def decode(self, json_str): # pylint: disable=W0221 d = super(SymbolsDecoder, self).decode(json_str) symbols = symbol.Symbols() for sym_key, sym_dict in d.iteritems(): sym = symbol.Symbol(sym_dict['name']) for source_info in sym_dict['source_info']: sym.AddSourceLineInfo(**source_info) symbols.symbols[sym_key] = sym return symbols
def testSymbols(self): archive = self._storage.OpenArchive('symbols', create=True) symbols = symbol.Symbols() # Symbol db is global per archive, no need to StartNewSnapshot. symbols.Add('foo.so', 1, symbol.Symbol('sym1', 'file1.c', 11)) symbols.Add('bar.so', 2, symbol.Symbol('sym2', 'file2.c', 12)) sym3 = symbol.Symbol('sym3', 'file2.c', 13) sym3.AddSourceLineInfo('outer_file.c', 23) symbols.Add('baz.so', 3, sym3) archive.StoreSymbols(symbols) symbols_deser = archive.LoadSymbols() self._DeepCompare(symbols, symbols_deser) self._storage.DeleteArchive('symbols')
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
def runTest(self): nheap = native_heap.NativeHeap() EXE_1_MM_BASE = 64 * PAGE_SIZE EXE_2_MM_BASE = 65 * PAGE_SIZE EXE_2_FILE_OFF = 8192 st1 = stacktrace.Stacktrace() st1.Add(nheap.GetStackFrame(EXE_1_MM_BASE)) st1.Add(nheap.GetStackFrame(EXE_1_MM_BASE + 4)) st2 = stacktrace.Stacktrace() st2.Add(nheap.GetStackFrame(EXE_1_MM_BASE)) st2.Add(nheap.GetStackFrame(EXE_2_MM_BASE + 4)) st2.Add(nheap.GetStackFrame(EXE_2_MM_BASE + PAGE_SIZE + 4)) # Check that GetStackFrames keeps one unique object instance per address. # This is to guarantee that the symbolization logic (SymbolizeUsingSymbolDB) # can cheaply iterate on distinct stack frames rather than re-processing # every stack frame for each allocation (and save memory as well). self.assertIs(st1[0], st2[0]) self.assertIsNot(st1[0], st1[1]) self.assertIsNot(st2[0], st2[1]) alloc1 = native_heap.Allocation(start=4, size=4, stack_trace=st1) alloc2 = native_heap.Allocation(start=4090, size=8, stack_trace=st1) alloc3 = native_heap.Allocation(start=8190, size=10000, stack_trace=st2) nheap.Add(alloc1) nheap.Add(alloc2) nheap.Add(alloc3) self.assertEqual(len(nheap.allocations), 3) self.assertIn(alloc1, nheap.allocations) self.assertIn(alloc2, nheap.allocations) self.assertIn(alloc3, nheap.allocations) ############################################################################ # Test the relativization (absolute address -> mmap + offset) logic. ############################################################################ mmap = memory_map mmap = memory_map.Map() mmap.Add( memory_map.MapEntry(EXE_1_MM_BASE, EXE_1_MM_BASE + PAGE_SIZE - 1, 'rw--', '/d/exe1', 0)) mmap.Add( memory_map.MapEntry(EXE_2_MM_BASE, EXE_2_MM_BASE + PAGE_SIZE - 1, 'rw--', 'exe2', EXE_2_FILE_OFF)) # Entry for EXE_3 is deliberately missing to check the fallback behavior. nheap.RelativizeStackFrames(mmap) self.assertEqual(st1[0].exec_file_rel_path, '/d/exe1') self.assertEqual(st1[0].exec_file_name, 'exe1') self.assertEqual(st1[0].offset, 0) self.assertEqual(st1[1].exec_file_rel_path, '/d/exe1') self.assertEqual(st1[1].exec_file_name, 'exe1') self.assertEqual(st1[1].offset, 4) self.assertEqual(st2[0].exec_file_rel_path, '/d/exe1') self.assertEqual(st2[0].exec_file_name, 'exe1') self.assertEqual(st2[0].offset, 0) self.assertEqual(st2[1].exec_file_rel_path, 'exe2') self.assertEqual(st2[1].exec_file_name, 'exe2') self.assertEqual(st2[1].offset, 4 + EXE_2_FILE_OFF) self.assertIsNone(st2[2].exec_file_rel_path) self.assertIsNone(st2[2].exec_file_name) self.assertIsNone(st2[2].offset) ############################################################################ # Test the symbolization logic. ############################################################################ syms = symbol.Symbols() syms.Add('/d/exe1', 0, symbol.Symbol('sym1', 'src1.c', 1)) # st1[0] syms.Add('exe2', 4 + EXE_2_FILE_OFF, symbol.Symbol('sym3')) # st2[1] nheap.SymbolizeUsingSymbolDB(syms) self.assertEqual(st1[0].symbol.name, 'sym1') self.assertEqual(st1[0].symbol.source_info[0].source_file_path, 'src1.c') self.assertEqual(st1[0].symbol.source_info[0].line_number, 1) # st1[1] should have no symbol info, because we didn't provide any above. self.assertIsNone(st1[1].symbol) # st2[0] and st1[0] were the same Frame. Expect identical symbols instances. self.assertIs(st2[0].symbol, st1[0].symbol) # st2[1] should have a symbols name, but no source line info. self.assertEqual(st2[1].symbol.name, 'sym3') self.assertEqual(len(st2[1].symbol.source_info), 0) # st2[2] should have no sym because we didn't even provide a mmap for exe3. self.assertIsNone(st2[2].symbol) ############################################################################ # Test the resident size calculation logic (intersects mmaps and allocs). ############################################################################ mmap.Add( memory_map.MapEntry(0, 8191, 'rw--', '', 0, resident_pages=[1])) mmap.Add( memory_map.MapEntry(8192, 12287, 'rw--', '', 0, resident_pages=[1])) # [12k, 16k] is deliberately missing to check the fallback behavior. mmap.Add( memory_map.MapEntry(16384, 20479, 'rw--', '', 0, resident_pages=[1])) nheap.CalculateResidentSize(mmap) # alloc1 [4, 8] is fully resident because it lays in the first resident 4k. self.assertEqual(alloc1.resident_size, 4) # alloc2 [4090, 4098] should have only 6 resident bytes ([4090,4096]), but # not the last two, which lay on the second page which is noijt resident. self.assertEqual(alloc2.resident_size, 6) # alloc3 [8190, 18190] is split as follows (* = resident): # [8190, 8192]: these 2 bytes are NOT resident, they lay in the 2nd page. # *[8192, 12288]: the 3rd page is resident and is fully covered by alloc3. # [12288, 16384]: the 4th page is fully covered as well, but not resident. # *[16384, 18190]: the 5th page is partially covered and resident. self.assertEqual(alloc3.resident_size, (12288 - 8192) + (18190 - 16384))