1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
#!/usr/bin/env drgn
# Copyright (c) Meta Platforms, Inc. and affiliates.
# SPDX-License-Identifier: LGPL-2.1-or-later
"""List the paths of all descendants of a cgroup v2"""
import argparse
from contextlib import contextmanager
import os
import sys
from collections import Counter
from drgn import cast
from drgn.helpers.common.type import enum_type_to_class
from drgn.helpers.linux import (
cgroup_bpf_prog_for_each,
cgroup_path,
css_for_each_descendant_pre,
fget,
find_task,
)
# Since Linux kernel commit 6fc88c354f3a ("bpf: Migrate cgroup_bpf to internal
# cgroup_bpf_attach_type enum") (in v5.15), the attach type is the
# cgroup-specific enum cgroup_bpf_attach_type. Before that, it was the generic
# enum bpf_attach_type.
try:
enum_cgroup_bpf_attach_type = prog.type("enum cgroup_bpf_attach_type")
except LookupError:
CgroupBpfAttachType = enum_type_to_class(
prog.type("enum bpf_attach_type"),
"CgroupBpfAttachType",
exclude=("__MAX_BPF_ATTACH_TYPE",),
)
else:
CgroupBpfAttachType = enum_type_to_class(
enum_cgroup_bpf_attach_type,
"CgroupBpfAttachType",
exclude=("CGROUP_BPF_ATTACH_TYPE_INVALID", "MAX_CGROUP_BPF_ATTACH_TYPE",),
)
CgroupSubsysId = enum_type_to_class(
prog.type("enum cgroup_subsys_id"),
"CgroupSubsysId",
exclude=("CGROUP_SUBSYS_COUNT",),
)
@contextmanager
def open_dir(*args, **kwds):
# Built-in open() context manager can't deal with directories.
fd = os.open(*args, **kwds)
try:
yield fd
finally:
os.close(fd)
def print_cgroup_bpf_progs(cgrp):
cgroup_printed = False
for attach_type in CgroupBpfAttachType:
attach_flags = cgrp.bpf.flags[attach_type.value].value_()
for prog in cgroup_bpf_prog_for_each(cgrp, attach_type.value):
prog_id = prog.aux.id.value_()
prog_name = prog.aux.name.string_().decode()
if not cgroup_printed:
print(cgroup_path(cgrp).decode())
cgroup_printed = True
print(
" {:<8} {:<30} {:<15} {:<15}".format(
prog_id, attach_type.name, attach_flags, prog_name
)
)
def get_cgroup(path):
task = find_task(prog, os.getpid())
try:
with open_dir(path, os.O_RDONLY) as fd:
file_ = fget(task, fd)
kn = cast("struct kernfs_node *", file_.f_path.dentry.d_inode.i_private)
return cast("struct cgroup *", kn.priv)
except FileNotFoundError as e:
raise argparse.ArgumentTypeError(e)
def cmd_tree(cgroup):
css = cgroup.self.address_of_()
for pos in css_for_each_descendant_pre(css):
if not pos.flags & prog["CSS_ONLINE"]:
continue
print(cgroup_path(pos.cgroup).decode())
def cmd_bpf(cgroup):
css = cgroup.self.address_of_()
for pos in css_for_each_descendant_pre(css):
if not pos.flags & prog["CSS_ONLINE"]:
continue
print_cgroup_bpf_progs(pos.cgroup)
def cmd_stat(cgroup):
stat = Counter()
stat_dying = Counter()
for ssid in CgroupSubsysId:
css = cgroup.subsys[ssid.value]
# XXX if subsys of offlined or cgroup rmdir'd under our hands we won't see its subtree
if not css:
continue
for pos in css_for_each_descendant_pre(css):
stat[ssid] +=1
if not pos.flags & prog["CSS_ONLINE"]:
stat_dying[ssid] += 1
for ssid in CgroupSubsysId:
if stat[ssid.value] == 0:
continue
print("nr_{:<30} {:>4}".format(
ssid.name,
stat[ssid.value]
)
)
for ssid in CgroupSubsysId:
if stat_dying[ssid.value] == 0:
continue
print("nr_dying_{:<24} {:>4}".format(
ssid.name,
stat_dying[ssid.value]
)
)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("command", choices=["tree", "bpf", "stat"])
parser.add_argument("cgroups", help="Cgroups", nargs="*", type=get_cgroup)
args = parser.parse_args()
if len(args.cgroups) == 0:
args.cgroups.append(prog["cgrp_dfl_root"].cgrp)
for cg in args.cgroups:
if len(args.cgroups) > 1:
print(cg.kn.name.string_())
locals()["cmd_" + args.command](cg)
|