[PATCH 32/36] tuna: Enhance show_threads -G to display cpuset names

John Kacur <[email protected]> Fri, 10 Jul 2026 10:15:10 -0400
Newsgroups org.kernel.vger.linux-rt-users
Message-ID <[email protected]>
Changed the cgroups display in 'tuna show_threads -G' to show
simplified cpuset names instead of full cgroup v2 paths, making
output more user-friendly and readable.

Changes:
- Added extract_cpuset_name() function to parse cgroup paths
  - Extracts first component: "0::/tuna0" → "tuna0"
  - System cgroups: "0::/system.slice/ssh.service" → "system.slice"
  - Root cgroup: "0::/" → "" (empty string)
- Changed column header from "cgroup" to "cpuset"
- Modified ps_show_thread() to use extracted cpuset name in display

This makes it much easier to see which cpuset a process belongs to
without having to parse long cgroup paths.

Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: John Kacur <[email protected]>
---
 tuna-cmd.py | 46 +++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 41 insertions(+), 5 deletions(-)

diff --git a/tuna-cmd.py b/tuna-cmd.py
index c2208e268c97..4703ea41f8d4 100755
--- a/tuna-cmd.py
+++ b/tuna-cmd.py
@@ -358,6 +358,41 @@ def save(cpu_list, thread_list, filename):
     tuna.generate_rtgroups(filename, kthreads, utils.get_nr_cpus())
 
 
+def extract_cpuset_name(cgroup_path):
+    """
+    Extract cpuset/cgroup name from full cgroup path for display.
+
+    Examples:
+        "0::/tuna0" → "tuna0"
+        "0::/" → ""
+        "0::/system.slice/ssh.service" → "system.slice"
+        "0::/user.slice/user-1000.slice/session-2.scope" → "user.slice"
+
+    Args:
+        cgroup_path: Full cgroup path from /proc/[pid]/cgroup
+
+    Returns:
+        First component of the cgroup path (cpuset name or top-level cgroup)
+    """
+    if not cgroup_path:
+        return ""
+
+    # Remove "0::" prefix
+    if cgroup_path.startswith("0::"):
+        cgroup_path = cgroup_path[3:]
+
+    # Remove leading slash
+    if cgroup_path.startswith("/"):
+        cgroup_path = cgroup_path[1:]
+
+    # Return empty string for root cgroup
+    if not cgroup_path:
+        return ""
+
+    # Return first component (cpuset name or top-level cgroup like "system.slice")
+    return cgroup_path.split('/')[0]
+
+
 def ps_show_header(has_ctxt_switch_info, cgroups=False):
     print("%7s %6s %5s %7s       %s" %
           (" ", " ", " ", _("thread"),
@@ -366,7 +401,7 @@ def ps_show_header(has_ctxt_switch_info, cgroups=False):
                                       has_ctxt_switch_info and " %9s %12s" % (
                                           "voluntary", "nonvoluntary")
                                       or "", "cmd"), end=' ')
-    print(" %7s" % ("cgroup") if cgroups else "")
+    print(" %7s" % ("cpuset") if cgroups else "")
 
 
 def ps_show_sockets(pid, ps, inodes, inode_re, indent=0):
@@ -419,7 +454,8 @@ def ps_show_thread(pid, affect_children, ps, has_ctxt_switch_info, sock_inodes,
 
     sched = tuna_sched.sched_str(os.sched_getscheduler(pid))[6:]
     rtprio = int(ps[pid]["stat"]["rt_priority"])
-    cgout = ps[pid]["cgroups"]
+    cgroup_path = ps[pid]["cgroups"]
+    cpuset_name = extract_cpuset_name(cgroup_path)
     cmd = ps[pid]["stat"]["comm"]
     users = ""
     if tuna.is_irq_thread(cmd):
@@ -451,10 +487,10 @@ def ps_show_thread(pid, affect_children, ps, has_ctxt_switch_info, sock_inodes,
 
     if cgroups:
         length = int(columns) - len(s1 + " ") - len(s2 + " ")
-        if len(" %9s" % cgout) <= length:
-            print("%s" % cgout)
+        if len(" %9s" % cpuset_name) <= length:
+            print("%s" % cpuset_name)
         else:
-            print("\n %s" % cgout + ("" if compact else "\n"))
+            print("\n %s" % cpuset_name + ("" if compact else "\n"))
     else:
         print()
 
-- 
2.54.0