Skip to content

Commit

Permalink
python: tools: Add tool for displaying a tree of PSI data
Browse files Browse the repository at this point in the history
Add a tool for displaying the PSI data for a cgroup hierarchy.

$ ./src/tools/cgpsitree.py -c cpu -f some-total -d 1
cpu some-total PSI data
/: 516797584
├── dev-hugepages.mount: 7
├── dev-mqueue.mount: 0
├── init.scope: 117064
├── machine.slice: 0
├── proc-sys-fs-binfmt_misc.mount: 3
├── sys-fs-fuse-connections.mount: 3
├── sys-kernel-config.mount: 0
├── sys-kernel-debug.mount: 109
├── sys-kernel-tracing.mount: 3
├── system.slice: 58977534
└── user.slice: 425882912

$ ./src/tools/cgpsitree.py -c memory -f full-avg60 -d 1
memory full-avg60 PSI data
/: 0.0
├── dev-hugepages.mount: 0.0
├── dev-mqueue.mount: 0.0
├── init.scope: 0.0
├── machine.slice: 0.0
├── proc-sys-fs-binfmt_misc.mount: 0.0
├── sys-fs-fuse-connections.mount: 0.0
├── sys-kernel-config.mount: 0.0
├── sys-kernel-debug.mount: 0.0
├── sys-kernel-tracing.mount: 0.0
├── system.slice: 0.0
└── user.slice: 0.0

Signed-off-by: Tom Hromatka <[email protected]>
  • Loading branch information
drakenclimber committed Apr 24, 2024
1 parent 3e08cb3 commit 3b46d78
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 1 deletion.
52 changes: 51 additions & 1 deletion src/python/libcgroup.pyx.m4
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ cdef class Cgroup:
""" Python object representing a libcgroup cgroup """
cdef cgroup.cgroup * _cgp
cdef public:
object name, controllers, version, path, children, settings, pids
object name, controllers, version, path, children, settings, pids, psi

@staticmethod
def cgroup_init():
Expand Down Expand Up @@ -127,6 +127,7 @@ cdef class Cgroup:
self.children = list()
self.settings = dict()
self.pids = list()
self.psi = dict()

def __str__(self):
out_str = "Cgroup {}\n".`format'(self.name)
Expand All @@ -138,6 +139,8 @@ cdef class Cgroup:
out_str += indent("pids = {}\n".`format'(`len'(self.pids)), 4)
for key, value in self.settings.items():
out_str += indent("settings[{}] = {}\n".`format'(key, value), 4)
for key, value in self.psi.items():
out_str += indent("psi[{}] = {}\n".`format'(key, value), 4)

return out_str

Expand Down Expand Up @@ -870,6 +873,35 @@ cdef class Cgroup:

self.settings[setting] = value

def _parse_psi_line(self, line):
entries = line.split()
for i, entry in enumerate(entries):
if i == 0:
continue
key = '{}-{}'.`format'(entries[0], entry.split('=')[0])

if 'total' in key:
self.psi[key] = int(entry.split('=')[1])
else:
self.psi[key] = float(entry.split('=')[1])

def get_psi(self, controller):
"""Get the PSI data for this cgroup and controller

Arguments:
controller - PSI data to obtain - cpu, memory, or io
"""
if not type(CgroupFile):
raise CgroupError('PSI data can only be gathered on cgroup directories: {}'.`format'(self.path))

setting = '{}.pressure'.`format'(controller)

self.add_setting(setting)
self.cgxget()

for line in self.controllers[controller].settings[setting].splitlines():
self._parse_psi_line(line)

def __dealloc__(self):
cgroup.cgroup_free(&self._cgp)

Expand Down Expand Up @@ -1036,6 +1068,24 @@ class LibcgroupTree(object):
else:
self.tree.show()

class LibcgroupPsiTree(LibcgroupTree):
def __init__(self, name, controller='cpu', depth=None, psi_field='some-avg10'):
super().__init__(name, version=Version.CGROUP_V2, files=False, depth=depth)

self.rootcg.get_psi(controller)
self.psi_field = psi_field

def walk_action(self, cg):
cg.get_psi(self.controller)
super().walk_action(cg)

def node_label(self, cg):
name = os.path.basename(cg.name)
if not `len'(name):
name = '/'

return '{}: {}'.`format'(name, cg.psi[self.psi_field])

float_metrics = ['%usr', '%system', '%guest', '%wait', '%CPU', '%MEM', 'minflt/s', 'majflt/s']
int_metrics = ['Time', 'UID', 'PID', 'CPU', 'RSS', 'threads', 'fd-nr']
str_metrics = ['Command']
Expand Down
41 changes: 41 additions & 0 deletions src/tools/cgpsitree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-2.1-only
#
# Display the PSI usage in a cgroup hierarchy
#
# Copyright (c) 2021-2024 Oracle and/or its affiliates.
# Author: Tom Hromatka <[email protected]>
#

from libcgroup import LibcgroupPsiTree
import argparse
import os

def parse_args():
parser = argparse.ArgumentParser("Libcgroup PSI Tree")
parser.add_argument('-C', '--cgroup', type=str, required=False, default=None,
help='Relative path to the cgroup of interest, e.g. machine.slice/foo.scope')
parser.add_argument('-c', '--controller', required=True,
help='PSI controller data to display. cpu, io, or memory')
parser.add_argument('-f', '--field', required=False, default='some-avg10',
help='Which PSI field to display, e.g. some-avg10, full-avg60, ...')
parser.add_argument('-d', '--depth', type=int, required=False, default=None,
help='Depth to recurse into the cgroup path. 0 == only this cgroup, 1 == this cgroup and its children, ...')

args = parser.parse_args()

return args

def main(args):
cgtree = LibcgroupPsiTree(args.cgroup, controller=args.controller, depth=args.depth,
psi_field=args.field)

cgtree.walk()
cgtree.build()

print('{} {} PSI data'.format(args.controller, args.field))
cgtree.show()

if __name__ == '__main__':
args = parse_args()
main(args)

0 comments on commit 3b46d78

Please sign in to comment.