示例#1
0
 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
示例#2
0
 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
示例#4
0
    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))