From 887d4f67097eead633cd012cf8d78f11cc3492e7 Mon Sep 17 00:00:00 2001 From: Siddhi Katage Date: Wed, 6 Dec 2023 08:18:30 +0000 Subject: [PATCH] task: add "ps" corelens module Co-authored-by: Partha Sarathi Satapathy Signed-off-by: Siddhi Katage --- drgn_tools/task.py | 207 ++++++++++++++++++++++++++++++++++++++++++++- tests/test_task.py | 9 +- 2 files changed, 213 insertions(+), 3 deletions(-) diff --git a/drgn_tools/task.py b/drgn_tools/task.py index 0ade61ad..dca2687f 100644 --- a/drgn_tools/task.py +++ b/drgn_tools/task.py @@ -9,13 +9,17 @@ and configurations. """ import argparse +from typing import Dict from typing import Iterable +from typing import Optional import drgn +from drgn import Architecture from drgn import Object from drgn import Program from drgn.helpers.common.format import escape_ascii_string from drgn.helpers.linux.cpumask import for_each_online_cpu +from drgn.helpers.linux.list import list_for_each_entry from drgn.helpers.linux.percpu import per_cpu from drgn.helpers.linux.pid import for_each_task from drgn.helpers.linux.sched import cpu_curr @@ -25,6 +29,12 @@ from drgn_tools.table import print_table from drgn_tools.util import has_member +rss_task_dict_global: Dict[int, int] = {} +rss_mm_dict_global = {} +rss_task_dict: Dict[int, Optional[int]] = {} + +ByteToKB = 1024 + def nanosecs_to_secs(nanosecs: int) -> float: """ @@ -185,6 +195,173 @@ def get_command(task: Object) -> str: return escape_ascii_string(task.comm.string_()) +def get_ppid(task: Object) -> int: + """ + :returns: Parent PID of the task + """ + return task.parent.pid.value_() + + +def build_rss_global(prog: Program) -> None: + """ + :Build the global rsss table + O(N) logic, used n total ps rss list + """ + MM_FILEPAGES = prog.constant("MM_FILEPAGES").value_() + MM_ANONPAGES = prog.constant("MM_ANONPAGES").value_() + try: + MM_SHMEMPAGES = prog.constant("MM_SHMEMPAGES").value_() + except LookupError: + MM_SHMEMPAGES = -1 + + for task in for_each_task(prog): + rss_task = task_filerss = task_anonrss = task_shmrss = 0 + + task_filerss = task.rss_stat.count[MM_FILEPAGES].value_() + task_anonrss = task.rss_stat.count[MM_ANONPAGES].value_() + if MM_SHMEMPAGES > 0: + task_shmrss = task.rss_stat.count[MM_SHMEMPAGES].value_() + else: + task_shmrss = 0 + + rss_task += task_filerss + task_anonrss + task_shmrss + + gid = task.tgid.value_() + try: + gid_rss = rss_task_dict_global[gid] + except KeyError: + gid_rss = 0 + else: + rss_task += gid_rss + + rss_task_dict_global[gid] = rss_task + + try: + filerss = task.mm.rss_stat.count[MM_FILEPAGES].counter.value_() + anonrss = task.mm.rss_stat.count[MM_ANONPAGES].counter.value_() + if MM_SHMEMPAGES > 0: + shmrss = task.mm.rss_stat.count[MM_SHMEMPAGES].counter.value_() + else: + shmrss = 0 + + rss_mm = filerss + anonrss + shmrss + except drgn.FaultError: + rss_mm = 0 + + rss_mm_dict_global[task.pid.value_()] = rss_mm + + +def get_rss(prog: Program, task: Object) -> int: + """ + :returns RSS value for a task + should be used for single task + """ + MM_FILEPAGES = prog.constant("MM_FILEPAGES").value_() + MM_ANONPAGES = prog.constant("MM_ANONPAGES").value_() + try: + MM_SHMEMPAGES = prog.constant("MM_SHMEMPAGES").value_() + except LookupError: + MM_SHMEMPAGES = -1 + + try: + filerss = task.mm.rss_stat.count[MM_FILEPAGES].counter.value_() + anonrss = task.mm.rss_stat.count[MM_ANONPAGES].counter.value_() + if MM_SHMEMPAGES > 0: + shmrss = task.mm.rss_stat.count[MM_SHMEMPAGES].counter.value_() + else: + shmrss = 0 + + rss_mm = filerss + anonrss + shmrss + + except drgn.FaultError: + rss_mm = 0 + + gid = task.tgid.value_() + + rss_task = rss_task_dict.get(gid) + + if gid in rss_task_dict: + rss_task = rss_task_dict[gid] + else: + rss_task = 0 + ltask = task.group_leader + + task_filerss = ltask.rss_stat.count[MM_FILEPAGES].value_() + task_anonrss = ltask.rss_stat.count[MM_ANONPAGES].value_() + if MM_SHMEMPAGES > 0: + task_shmrss = ltask.rss_stat.count[MM_SHMEMPAGES].value_() + else: + task_shmrss = 0 + + rss_task += task_filerss + task_anonrss + task_shmrss + + for gtask in list_for_each_entry( + "struct task_struct", + task.group_leader.thread_group.address_of_(), + "thread_group", + ): + task_filerss = gtask.rss_stat.count[MM_FILEPAGES].value_() + task_anonrss = gtask.rss_stat.count[MM_ANONPAGES].value_() + if MM_SHMEMPAGES > 0: + task_shmrss = gtask.rss_stat.count[MM_SHMEMPAGES].value_() + else: + task_shmrss = 0 + + rss_task += task_filerss + task_anonrss + task_shmrss + + rss_task_dict[gid] = rss_task + + rss = rss_mm + rss_task + page_size = prog["PAGE_SIZE"].value_() + rss_kb = (rss * page_size) // ByteToKB + + return rss_kb + + +def get_pct_mem(prog: Program, task: Object) -> str: + """ + :returns %MEM value + """ + gid = task.tgid.value_() + rss_task = rss_task_dict_global[gid] + rss_mm = rss_mm_dict_global[task.pid.value_()] + tot_rss = rss_mm + rss_task + + page_shift = prog["PAGE_SHIFT"].value_() + high_mem = prog["high_memory"].value_() + + if prog.platform.arch == Architecture.X86_64: + try: + page_offset_base = prog["page_offset_base"].value_() + except KeyError: + # Prior to 021182e52fe0 ("x86/mm: Enable KASLR for physical mapping + # memory regions"), page_offset_base did not exist and there was + # just a constant for the beginning of the memory map. + page_offset_base = 0xFFFF880000000000 + + total_pages = (high_mem - page_offset_base) >> page_shift + else: + raise NotImplementedError( + "%MEM calculation is not (yet) implemented for aarch64" + ) + mem = (tot_rss * 100) / total_pages + pct_mem = "%.1f" % mem + + return pct_mem + + +def get_vmem(prog: Program, task: Object) -> float: + """ + :returns VSZ value + """ + try: + page_size = prog["PAGE_SIZE"].value_() + vmem = (task.mm.total_vm.value_() * page_size) // ByteToKB + except drgn.FaultError: + vmem = 0 + return vmem + + def show_tasks_last_runtime(tasks: Iterable[Object]) -> None: """ Display task information in their last arrival order. @@ -203,6 +380,30 @@ def show_tasks_last_runtime(tasks: Iterable[Object]) -> None: print_table(rows) +def show_taskinfo(prog: Program, tasks: Iterable[Object]) -> None: + """ + Display task information. + """ + rows = [["PID", "PPID", "CPU", "TASK", "ST", "%MEM", "VSZ", "RSS", "COMM"]] + tasks = list(tasks) + tasks.sort(key=get_pid) + for t in tasks: + rows.append( + [ + str(get_pid(t)), + str(get_ppid(t)), + str(task_cpu(t)), + hex(t.value_()), + task_state_to_char(t), + get_pct_mem(prog, t), + str(get_vmem(prog, t)), + str(get_rss(prog, t)), + get_command(t), + ] + ) + print_table(rows) + + class Taskinfo(CorelensModule): """ Corelens Module for ps @@ -221,7 +422,9 @@ def add_args(self, parser: argparse.ArgumentParser) -> None: ) def run(self, prog: Program, args: argparse.Namespace) -> None: + build_rss_global(prog) + tasks = for_each_task(prog) if args.last_run: - show_tasks_last_runtime(for_each_task(prog)) + show_tasks_last_runtime(tasks) else: - raise NotImplementedError("currently, only ps -m is implemented") + show_taskinfo(prog, tasks) diff --git a/tests/test_task.py b/tests/test_task.py index e5ea5a39..a5ce3bb2 100644 --- a/tests/test_task.py +++ b/tests/test_task.py @@ -1,9 +1,16 @@ # Copyright (c) 2023, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +from drgn.helpers.linux.pid import find_task from drgn.helpers.linux.pid import for_each_task from drgn_tools import task -def test_task_last_runtime(prog): +def test_show_taskinfo(prog): + task.build_rss_global(prog) + print("===== Task information in their last arrival order =====") task.show_tasks_last_runtime(for_each_task(prog)) + print("===== Display task information =====") + task.show_taskinfo(prog, for_each_task(prog)) + print("===== RSS value for PID 1 =====") + print(task.get_rss(prog, find_task(prog, 1)))