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
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
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 []
def test_for_each_task(self): pid = os.getpid() self.assertTrue(any(task.pid == pid for task in for_each_task(self.prog)))
def no_input(self) -> Iterable[drgn.Object]: yield from for_each_task(sdb.get_prog())
"""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}')
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 []
def no_input(self) -> Iterable[drgn.Object]: self.validate_context() yield from filter(self.match_stack, for_each_task(sdb.get_prog()))