Ejemplo n.º 1
0
def find_task(name):
    print('PID        COMM')
    for task in for_each_task(prog):
        pid = task.pid.value_()
        comm = task.comm.string_().decode()
        if comm == "ovs-vswitchd":
            print(f'{pid:<10} {comm}')
            return task
Ejemplo n.º 2
0
    def test_for_each_task(self):
        NUM_PROCS = 12
        barrier = Barrier(NUM_PROCS + 1)

        def proc_func():
            barrier.wait()

        try:
            procs = [Process(target=proc_func) for _ in range(NUM_PROCS)]
            for proc in procs:
                proc.start()
            pids = {task.pid.value_() for task in for_each_task(self.prog)}
            for proc in procs:
                self.assertIn(proc.pid, pids)
            self.assertIn(os.getpid(), pids)
            barrier.wait()
        except BaseException:
            barrier.abort()
            for proc in procs:
                proc.terminate()
            raise
Ejemplo n.º 3
0
    def call(self, objs: Iterable[drgn.Object]) -> Iterable[drgn.Object]:
        # pylint: disable=too-many-locals
        # pylint: disable=too-many-branches

        #
        # As the exception explains the code that follows this statement
        # only works for linux kernel targets (crash dumps or live systems).
        # When support for userland is added we can factor the kernel code
        # that follows into its own function and switch to the correct
        # codepath depending on the target.
        #
        if not self.prog.flags & drgn.ProgramFlags.IS_LINUX_KERNEL:
            raise sdb.CommandError(self.name,
                                   "userland targets are not supported yet")
        self.validate_args(self.prog, self.args)

        #
        # Resolve TSTATE shortcut and/or sanitize it to standard uppercase
        # notation if it exists.
        #
        if self.args.tstate:
            if self.args.tstate in Stacks.TASK_STATE_SHORTCUTS:
                self.args.tstate = Stacks.TASK_STATES[
                    Stacks.TASK_STATE_SHORTCUTS[self.args.tstate]]
            else:
                self.args.tstate = self.args.tstate.upper()

        mod_start, mod_end = -1, -1
        if self.args.module:
            mod_start, mod_size = Stacks.find_module_memory_segment(
                self.prog, self.args.module)
            assert mod_start != -1
            mod_end = mod_start + mod_size

        header = "{:<18} {:<16s}".format("TASK_STRUCT", "STATE")
        if not self.args.all:
            header += " {:>6s}".format("COUNT")
        print(header)
        print("=" * 42)

        #
        # We inspect and group the tasks by recording their state and
        # stack frames once in the following loop. We do this because
        # on live systems state can change under us, thus running
        # something like self.prog.stack_trace(task) twice (once for
        # grouping and once for printing) could yield different stack
        # traces resulting into misleading output.
        #
        stack_aggr = defaultdict(list)
        for task in for_each_task(self.prog):
            stack_key = [Stacks.task_struct_get_state(task)]
            for frame in self.prog.stack_trace(task):
                stack_key.append(frame.pc)
            stack_aggr[tuple(stack_key)].append(task)

        for stack_key, tasks in sorted(stack_aggr.items(),
                                       key=lambda x: len(x[1]),
                                       reverse=True):
            task_state = stack_key[0]
            if self.args.tstate and self.args.tstate != task_state:
                continue

            stacktrace_info = ""
            if self.args.all:
                for task in tasks:
                    stacktrace_info += "{:<18s} {:<16s}\n".format(
                        hex(task.value_()), task_state)
            else:
                stacktrace_info += "{:<18s} {:<16s} {:6d}\n".format(
                    hex(tasks[0].value_()), task_state, len(tasks))

            mod_match, func_match = False, False
            for frame_pc in stack_key[1:]:
                if mod_start != -1 and mod_start <= frame_pc < mod_end:
                    mod_match = True

                try:
                    sym = self.prog.symbol(frame_pc)
                    func, offset = sym.name, frame_pc - sym.address
                    if self.args.function and self.args.function == func:
                        func_match = True
                except LookupError:
                    func, offset = hex(frame_pc), 0x0

                #
                # As a potential future item, we may want to print
                # the frame with the module where the pc/function
                # belongs to. For example:
                #     txg_sync_thread+0x15e [zfs]
                #
                stacktrace_info += "{:18s}{}+{}\n".format("", func, hex(offset))

            if mod_start != -1 and not mod_match:
                continue
            if self.args.function and not func_match:
                continue
            print(stacktrace_info)
        return []
Ejemplo n.º 4
0
 def test_for_each_task(self):
     pid = os.getpid()
     self.assertTrue(any(task.pid == pid for task in for_each_task(self.prog)))
Ejemplo n.º 5
0
 def no_input(self) -> Iterable[drgn.Object]:
     yield from for_each_task(sdb.get_prog())
Ejemplo n.º 6
0
"""A simplified implementation of ps(1) using drgn"""

from drgn.helpers.linux.pid import for_each_task

print('PID        COMM')
for task in for_each_task(prog):
    pid = task.pid.value_()
    comm = task.comm.string_().decode()
    print(f'{pid:<10} {comm}')
Ejemplo n.º 7
0
    def _call(self, objs: Iterable[drgn.Object]) -> Iterable[drgn.Object]:
        # pylint: disable=too-many-locals
        # pylint: disable=too-many-branches
        # pylint: disable=too-many-statements

        #
        # As the exception explains the code that follows this statement
        # only works for linux kernel targets (crash dumps or live systems).
        # When support for userland is added we can factor the kernel code
        # that follows into its own function and switch to the correct
        # codepath depending on the target.
        #
        if not sdb.get_target_flags() & drgn.ProgramFlags.IS_LINUX_KERNEL:
            raise sdb.CommandError(self.name,
                                   "userland targets are not supported yet")
        self.validate_args(self.args)

        #
        # Resolve TSTATE shortcut and/or sanitize it to standard uppercase
        # notation if it exists.
        #
        if self.args.tstate:
            if self.args.tstate in Stacks.TASK_STATE_SHORTCUTS:
                self.args.tstate = Stacks.TASK_STATES[
                    Stacks.TASK_STATE_SHORTCUTS[self.args.tstate]]
            else:
                self.args.tstate = self.args.tstate.upper()

        mod_start, mod_end = -1, -1
        if self.args.module:
            mod_start, mod_size = Stacks.find_module_memory_segment(
                self.args.module)
            assert mod_start != -1
            mod_end = mod_start + mod_size

        header = "{:<18} {:<16s}".format("TASK_STRUCT", "STATE")
        if not self.args.all:
            header += " {:>6s}".format("COUNT")
        print(header)
        print("=" * 42)

        #
        # We inspect and group the tasks by recording their state and
        # stack frames once in the following loop. We do this because
        # on live systems state can change under us, thus running
        # something like sdb.get_prog().stack_trace(task) twice (once for
        # grouping and once for printing) could yield different stack
        # traces resulting into misleading output.
        #
        stack_aggr: Dict[Any, List[drgn.Object]] = defaultdict(list)
        for task in for_each_task(sdb.get_prog()):
            stack_key = [Stacks.task_struct_get_state(task)]
            try:
                for frame in sdb.get_prog().stack_trace(task):
                    stack_key.append(frame.pc)
            except ValueError:
                #
                # Unwinding the stack of a running/runnable task will
                # result in an exception. Since we expect some tasks to
                # be running, we silently ignore this case, and move on.
                #
                # Unfortunately, the exception thrown in this case is a
                # generic "ValueError" exception, so we may wind up
                # masking other "ValueError" exceptions that are not due
                # to unwinding the stack of a running task.
                #
                # We can't check the state of the task here, and verify
                # it's in the "R" state, since that state can change in
                # between the point where the "ValueError" exception was
                # originally raised, and here where we'd verify the
                # state of the task; i.e. it could have concurrently
                # transitioned from running to some other state.
                #
                pass

            stack_aggr[tuple(stack_key)].append(task)

        for stack_key, tasks in sorted(stack_aggr.items(),
                                       key=lambda x: len(x[1]),
                                       reverse=True):
            task_state = stack_key[0]
            if self.args.tstate and self.args.tstate != task_state:
                continue

            stacktrace_info = ""
            if self.args.all:
                for task in tasks:
                    stacktrace_info += "{:<18s} {:<16s}\n".format(
                        hex(task.value_()), task_state)
            else:
                stacktrace_info += "{:<18s} {:<16s} {:6d}\n".format(
                    hex(tasks[0].value_()), task_state, len(tasks))

            mod_match, func_match = False, False

            #
            # Note on the type-check being ignored:
            # The original `stack_key` type is a list where the first
            # element is a string and the rest of them are integers
            # but this is not easily expressed in mypy, thus we ignore
            # the assignment error below.
            #
            frame_pcs: List[int] = stack_key[1:]  #type: ignore[assignment]
            for frame_pc in frame_pcs:
                if mod_start != -1 and mod_start <= frame_pc < mod_end:
                    mod_match = True

                try:
                    sym = sdb.get_symbol(frame_pc)
                    func, offset = sym.name, frame_pc - sym.address
                    if self.args.function and self.args.function == func:
                        func_match = True
                except LookupError:
                    func, offset = hex(frame_pc), 0x0

                #
                # As a potential future item, we may want to print
                # the frame with the module where the pc/function
                # belongs to. For example:
                #     txg_sync_thread+0x15e [zfs]
                #
                stacktrace_info += "{:18s}{}+{}\n".format("", func, hex(offset))

            if mod_start != -1 and not mod_match:
                continue
            if self.args.function and not func_match:
                continue
            print(stacktrace_info)
        return []
Ejemplo n.º 8
0
 def no_input(self) -> Iterable[drgn.Object]:
     self.validate_context()
     yield from filter(self.match_stack, for_each_task(sdb.get_prog()))