예제 #1
0
def inactive_objs(cache: drgn.Object) -> int:
    assert cache.type_.type_name() == 'struct kmem_cache *'
    node = cache.node[0].partial
    free = 0
    for page in list_for_each_entry("struct page", node.address_of_(), "lru"):
        free += page.objects.value_() - page.inuse.value_()

    if not is_root_cache(cache):
        return free

    for child in list_for_each_entry("struct kmem_cache",
                                     cache.memcg_params.children.address_of_(),
                                     "memcg_params.children_node"):
        free += inactive_objs(child)
    return free
예제 #2
0
 def _call(self, objs: Iterable[drgn.Object]) -> Iterable[drgn.Object]:
     sname = get_valid_struct_name(self, self.args.struct_name)
     for obj in objs:
         try:
             yield from list_for_each_entry(sname, obj, self.args.member)
         except LookupError as err:
             raise sdb.CommandError(self.name, str(err))
예제 #3
0
파일: cgroup.py 프로젝트: osandov/drgn
def css_next_child(pos: Object, parent: Object) -> Object:
    """
    Get the next child (or ``NULL`` if there is none) of the given parent
    starting from the given position (``NULL`` to initiate traversal).

    :param pos: ``struct cgroup_subsys_state *``
    :param parent: ``struct cgroup_subsys_state *``
    :return: ``struct cgroup_subsys_state *``
    """
    if not pos:
        next_ = container_of(
            parent.children.next, "struct cgroup_subsys_state", "sibling"
        )
    elif not (pos.flags & pos.prog_["CSS_RELEASED"]):
        next_ = container_of(pos.sibling.next, "struct cgroup_subsys_state", "sibling")
    else:
        serial_nr = pos.serial_nr.value_()  # Read once and cache.
        for next_ in list_for_each_entry(
            "struct cgroup_subsys_state", parent.children.address_of_(), "sibling"
        ):
            if next_.serial_nr > serial_nr:
                break

    if next_.sibling.address_of_() != parent.children.address_of_():
        return next_
    return NULL(next_.prog_, "struct cgroup_subsys_state *")
예제 #4
0
파일: fs.py 프로젝트: osandov/drgn
def for_each_mount(
    prog_or_ns: Union[Program, Object],
    src: Optional[Path] = None,
    dst: Optional[Path] = None,
    fstype: Optional[Union[str, bytes]] = None,
) -> Iterator[Object]:
    """
    Iterate over all of the mounts in a given namespace.

    :param prog_or_ns: ``struct mnt_namespace *`` to iterate over, or
        :class:`Program` to iterate over initial mount namespace.
    :param src: Only include mounts with this source device name.
    :param dst: Only include mounts with this destination path.
    :param fstype: Only include mounts with this filesystem type.
    :return: Iterator of ``struct mount *`` objects.
    """
    if isinstance(prog_or_ns, Program):
        ns = prog_or_ns["init_task"].nsproxy.mnt_ns
    else:
        ns = prog_or_ns
    if src is not None:
        src = os.fsencode(src)
    if dst is not None:
        dst = os.fsencode(dst)
    if fstype:
        fstype = os.fsencode(fstype)
    for mnt in list_for_each_entry("struct mount", ns.list.address_of_(),
                                   "mnt_list"):
        if ((src is None or mount_src(mnt) == src)
                and (dst is None or mount_dst(mnt) == dst)
                and (fstype is None or mount_fstype(mnt) == fstype)):
            yield mnt
예제 #5
0
def for_each_mount(prog_or_ns, src=None, dst=None, fstype=None):
    """
    .. c:function:: for_each_mount(struct mnt_namespace *ns, char *src, char *dst, char *fstype)

    Iterate over all of the mounts in a given namespace. If given a
    :class:`Program` instead, the initial mount namespace is used. returned
    mounts can be filtered by source, destination, or filesystem type, all of
    which are encoded using :func:`os.fsencode()`.

    :return: Iterator of ``struct mount *`` objects.
    """
    if isinstance(prog_or_ns, Program):
        ns = prog_or_ns['init_task'].nsproxy.mnt_ns
    else:
        ns = prog_or_ns
    if src is not None:
        src = os.fsencode(src)
    if dst is not None:
        dst = os.fsencode(dst)
    if fstype:
        fstype = os.fsencode(fstype)
    for mnt in list_for_each_entry('struct mount', ns.list.address_of_(),
                                   'mnt_list'):
        if ((src is None or mount_src(mnt) == src)
                and (dst is None or mount_dst(mnt) == dst)
                and (fstype is None or mount_fstype(mnt) == fstype)):
            yield mnt
예제 #6
0
파일: block.py 프로젝트: sukidoke/drgn
def _for_each_block_device(prog):
    try:
        class_in_private = prog.cache['knode_class_in_device_private']
    except KeyError:
        # We need a proper has_member(), but this is fine for now.
        class_in_private = any(member[1] == 'knode_class' for member in
                               prog.type('struct device_private').members)
        prog.cache['knode_class_in_device_private'] = class_in_private
    devices = prog['block_class'].p.klist_devices.k_list.address_of_()
    if class_in_private:
        for device_private in list_for_each_entry('struct device_private', devices,
                                                  'knode_class.n_node'):
            yield device_private.device
    else:
        yield from list_for_each_entry('struct device', devices,
                                       'knode_class.n_node')
예제 #7
0
def path_lookup(prog_or_root, path, allow_negative=False):
    """
    .. c:function:: struct path path_lookup(struct path *root, const char *path, bool allow_negative)

    Look up the given path name relative to the given root directory. If given
    a :class:`Program` instead of a ``struct path``, the initial root
    filesystem is used.

    :param bool allow_negative: Whether to allow returning a negative dentry
        (i.e., a dentry for a non-existent path).
    :raises Exception: if the dentry is negative and ``allow_negative`` is
        ``False``, or if the path is not present in the dcache. The latter does
        not necessarily mean that the path does not exist; it may be uncached.
        On a live system, you can make the kernel cache the path by accessing
        it (e.g., with :func:`open()` or :func:`os.stat()`):

        >>> path_lookup(prog, '/usr/include/stdlib.h')
        ...
        Exception: could not find '/usr/include/stdlib.h' in dcache
        >>> open('/usr/include/stdlib.h').close()
        >>> path_lookup(prog, '/usr/include/stdlib.h')
        (struct path){
                .mnt = (struct vfsmount *)0xffff8b70413cdca0,
                .dentry = (struct dentry *)0xffff8b702ac2c480,
        }
    """
    if isinstance(prog_or_root, Program):
        prog_or_root = prog_or_root["init_task"].fs.root
    mnt = root_mnt = container_of(prog_or_root.mnt.read_(), "struct mount",
                                  "mnt")
    dentry = root_dentry = prog_or_root.dentry.read_()
    components = os.fsencode(path).split(b"/")
    for i, component in enumerate(components):
        if component == b"" or component == b".":
            continue
        elif component == b"..":
            mnt, dentry = _follow_dotdot(mnt, dentry, root_mnt, root_dentry)
        else:
            for child in list_for_each_entry("struct dentry",
                                             dentry.d_subdirs.address_of_(),
                                             "d_child"):
                if child.d_name.name.string_() == component:
                    dentry = child
                    break
            else:
                failed_path = os.fsdecode(b"/".join(components[:i + 1]))
                raise Exception(f"could not find {failed_path!r} in dcache")
            mnt, dentry = _follow_mount(mnt, dentry)
    if not allow_negative and not dentry.d_inode:
        failed_path = os.fsdecode(b"/".join(components))
        raise Exception(f"{failed_path!r} dentry is negative")
    return Object(
        mnt.prog_,
        "struct path",
        value={
            "mnt": mnt.mnt.address_of_(),
            "dentry": dentry,
        },
    )
예제 #8
0
파일: slab.py 프로젝트: osandov/drgn
def for_each_slab_cache(prog: Program) -> Iterator[Object]:
    """
    Iterate over all slab caches.

    :return: Iterator of ``struct kmem_cache *`` objects.
    """
    return list_for_each_entry("struct kmem_cache",
                               prog["slab_caches"].address_of_(), "list")
예제 #9
0
파일: block.py 프로젝트: shiloong/drgn
def _for_each_block_device(prog: Program) -> Iterator[Object]:
    try:
        class_in_private = prog.cache["knode_class_in_device_private"]
    except KeyError:
        # Linux kernel commit 570d0200123f ("driver core: move
        # device->knode_class to device_private") (in v5.1) moved the list
        # node.
        class_in_private = prog.type("struct device_private").has_member("knode_class")
        prog.cache["knode_class_in_device_private"] = class_in_private
    devices = prog["block_class"].p.klist_devices.k_list.address_of_()
    if class_in_private:
        for device_private in list_for_each_entry(
            "struct device_private", devices, "knode_class.n_node"
        ):
            yield device_private.device
    else:
        yield from list_for_each_entry("struct device", devices, "knode_class.n_node")
예제 #10
0
파일: block.py 프로젝트: sravyamks/drgn
def _for_each_block_device(prog):
    try:
        class_in_private = prog.cache["knode_class_in_device_private"]
    except KeyError:
        # We need a proper has_member(), but this is fine for now.
        class_in_private = any(
            member.name == "knode_class"
            for member in prog.type("struct device_private").members)
        prog.cache["knode_class_in_device_private"] = class_in_private
    devices = prog["block_class"].p.klist_devices.k_list.address_of_()
    if class_in_private:
        for device_private in list_for_each_entry("struct device_private",
                                                  devices,
                                                  "knode_class.n_node"):
            yield device_private.device
    else:
        yield from list_for_each_entry("struct device", devices,
                                       "knode_class.n_node")
예제 #11
0
def for_each_net(prog: Program) -> Iterator[Object]:
    """
    Iterate over all network namespaces in the system.

    :return: Iterator of ``struct net *`` objects.
    """
    for net in list_for_each_entry("struct net",
                                   prog["net_namespace_list"].address_of_(),
                                   "list"):
        yield net
예제 #12
0
def for_each_partial_slab_in_cache(
        cache: drgn.Object) -> Iterable[drgn.Object]:
    assert sdb.type_canonical_name(cache.type_) == 'struct kmem_cache *'

    node = cache.node[0].partial  # assumption nr_node_ids == 0
    yield from list_for_each_entry("struct page", node.address_of_(), "lru")

    if is_root_cache(cache):
        for child in for_each_child_cache(cache):
            yield from for_each_partial_slab_in_cache(child)
예제 #13
0
def objs(cache: drgn.Object) -> int:
    assert cache.type_.type_name() == 'struct kmem_cache *'
    count = cache.node[0].total_objects.counter.value_()
    if not is_root_cache(cache):
        return count
    for child in list_for_each_entry("struct kmem_cache",
                                     cache.memcg_params.children.address_of_(),
                                     "memcg_params.children_node"):
        count += objs(child)
    return count
예제 #14
0
def nr_slabs(cache: drgn.Object) -> int:
    assert cache.type_.type_name() == 'struct kmem_cache *'
    nslabs = cache.node[0].nr_slabs.counter.value_()
    if not is_root_cache(cache):
        return nslabs
    for child in list_for_each_entry("struct kmem_cache",
                                     cache.memcg_params.children.address_of_(),
                                     "memcg_params.children_node"):
        nslabs += nr_slabs(child)
    return nslabs
예제 #15
0
파일: test_list.py 프로젝트: osandov/drgn
 def test_list_for_each_entry(self):
     self.assertEqual(
         list(
             list_for_each_entry("struct drgn_test_list_entry", self.empty,
                                 "node")),
         [],
     )
     self.assertEqual(
         list(
             list_for_each_entry("struct drgn_test_list_entry", self.full,
                                 "node")),
         [self.entry(i) for i in range(self.num_entries)],
     )
     self.assertEqual(
         list(
             list_for_each_entry("struct drgn_test_list_entry",
                                 self.singular, "node")),
         [self.singular_entry],
     )
예제 #16
0
def for_each_partition(prog):
    """
    Iterate over all partitions in the system.

    :return: Iterator of ``struct hd_struct *`` objects.
    """
    devices = prog['block_class'].p.klist_devices.k_list.address_of_()
    for device in list_for_each_entry('struct device', devices,
                                      'knode_class.n_node'):
        yield container_of(device, 'struct hd_struct', '__dev')
예제 #17
0
def inactive_objs(cache: drgn.Object) -> int:
    assert sdb.type_canonical_name(cache.type_) == 'struct kmem_cache *'
    node = cache.node[0].partial  # assumption nr_node_ids == 0
    free = 0
    for page in list_for_each_entry("struct page", node.address_of_(), "lru"):
        free += page.objects.value_() - page.inuse.value_()
    if is_root_cache(cache):
        for child in for_each_child_cache(cache):
            free += inactive_objs(child)
    return free
예제 #18
0
def for_each_partial_slab_in_cache(
        cache: drgn.Object) -> Iterable[drgn.Object]:
    assert sdb.type_canonical_name(cache.type_) == 'struct kmem_cache *'
    for node in for_each_node(cache):
        node_partial = node.partial
        yield from list_for_each_entry("struct page",
                                       node_partial.address_of_(), "lru")

    if is_root_cache(cache):
        for child in for_each_child_cache(cache):
            yield from for_each_partial_slab_in_cache(child)
예제 #19
0
def for_each_disk(prog):
    """
    Iterate over all disks in the system.

    :return: Iterator of ``struct gendisk *`` objects.
    """
    devices = prog['block_class'].p.klist_devices.k_list.address_of_()
    disk_type = prog['disk_type'].address_of_()
    for device in list_for_each_entry('struct device', devices,
                                      'knode_class.n_node'):
        if device.type == disk_type:
            yield container_of(device, 'struct gendisk', 'part0.__dev')
예제 #20
0
파일: bpf.py 프로젝트: cneira/drgn
def cgroup_bpf_prog_for_each(cgrp, bpf_attach_type):
    """
    .. c:function:: cgroup_bpf_prog_for_each(struct cgroup *cgrp, int bpf_attach_type)

    Iterate over all cgroup bpf programs of the given attach type attached to
    the given cgroup.

    :return: Iterator of ``struct bpf_prog *`` objects.
    """
    progs_head = cgrp.bpf.progs[bpf_attach_type]
    for pl in list_for_each_entry("struct bpf_prog_list",
                                  progs_head.address_of_(), "node"):
        yield pl.prog
예제 #21
0
파일: bpf.py 프로젝트: pzakha/drgn
def cgroup_bpf_prog_for_each(cgrp: Object,
                             bpf_attach_type: IntegerLike) -> Iterator[Object]:
    """
    Iterate over all cgroup BPF programs of the given attach type attached to
    the given cgroup.

    :param cgrp: ``struct cgroup *``
    :param bpf_attach_type: ``enum bpf_attach_type``
    :return: Iterator of ``struct bpf_prog *`` objects.
    """
    progs_head = cgrp.bpf.progs[bpf_attach_type]
    for pl in list_for_each_entry("struct bpf_prog_list",
                                  progs_head.address_of_(), "node"):
        yield pl.prog
예제 #22
0
def for_each_object_in_spl_cache(cache: drgn.Object) -> Iterable[drgn.Object]:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    #
    # ZFSonLinux initially implemented OFFSLAB caches for certain cases
    # that never showed up and thus have never been used in practice.
    # Ensure here that we are not looking at such a cache.
    #
    if 'KMC_OFFSLAB' in list(for_each_slab_flag_in_cache(cache)):
        raise sdb.CommandError("spl_caches",
                               "KMC_OFFSLAB caches are not supported")

    for slab_list in [cache.skc_complete_list, cache.skc_partial_list]:
        for slab in drgn_list.list_for_each_entry("spl_kmem_slab_t",
                                                  slab_list.address_of_(),
                                                  "sks_list"):
            yield from for_each_onslab_object_in_slab(slab)
예제 #23
0
파일: stacks.py 프로젝트: tonynguien/sdb
    def find_module_memory_segment(prog: drgn.Program,
                                   mod_name: str) -> Tuple[int, int]:
        """
        Looks for the segment in memory where `mod_name` is
        loaded within `prog`.

        Returns:
            (<base_offset>, <size>) if `mod_name` is found.
            (-1, 0) otherwise.
        """
        for mod in list_for_each_entry('struct module',
                                       prog['modules'].address_of_(), 'list'):
            if mod.name.string_().decode("utf-8") == mod_name:
                return (mod.core_layout.base.value_(),
                        mod.core_layout.size.value_())
        return (-1, 0)
예제 #24
0
파일: test_list.py 프로젝트: osandov/drgn
 def test_validate_list_for_each_entry(self):
     for head in (self.empty, self.full, self.singular):
         self.assertEqual(
             list(
                 validate_list_for_each_entry("struct drgn_test_list_entry",
                                              head, "node")),
             list(
                 list_for_each_entry("struct drgn_test_list_entry", head,
                                     "node")),
         )
     self.assertRaises(
         ValidationError,
         collections.deque,
         validate_list_for_each_entry("struct drgn_test_list_entry",
                                      self.corrupted, "node"),
         0,
     )
예제 #25
0
    def walk(self, blkcg, q_id, parent_path):
        if not self.include_dying and \
           not (blkcg.css.flags.value_() & prog['CSS_ONLINE'].value_()):
            return

        name = BlkgIterator.blkcg_name(blkcg)
        path = parent_path + '/' + name if parent_path else name
        blkg = drgn.Object(prog, 'struct blkcg_gq',
                           address=radix_tree_lookup(blkcg.blkg_tree, q_id))
        if not blkg.address_:
            return

        self.blkgs.append((path if path else '/', blkg))

        for c in list_for_each_entry('struct blkcg',
                                     blkcg.css.children.address_of_(), 'css.sibling'):
            self.walk(c, q_id, path)
예제 #26
0
def qdisc_lookup(dev: Object, major: IntegerLike) -> Object:
    """
    Get a Qdisc from a device and a major handle number.  It is worth noting
    that conventionally handles are hexadecimal, e.g. ``10:`` in a ``tc``
    command means major handle 0x10.

    :param dev: ``struct net_device *``
    :param major: Qdisc major handle number.
    :return: ``struct Qdisc *`` (``NULL`` if not found)
    """
    major = operator.index(major) << 16

    roots = [dev.qdisc]
    if dev.ingress_queue:
        roots.append(dev.ingress_queue.qdisc_sleeping)

    # Since Linux kernel commit 59cc1f61f09c ("net: sched: convert qdisc linked
    # list to hashtable") (in v4.7), a device's child Qdiscs are maintained in
    # a hashtable in its struct net_device. Before that, they are maintained in
    # a linked list in their root Qdisc.
    use_hashtable = dev.prog_.type("struct net_device").has_member(
        "qdisc_hash")

    for root in roots:
        if root.handle == major:
            return root

        if use_hashtable:
            for head in root.dev_queue.dev.qdisc_hash:
                for qdisc in hlist_for_each_entry("struct Qdisc",
                                                  head.address_of_(), "hash"):
                    if qdisc.handle == major:
                        return qdisc
        else:
            for qdisc in list_for_each_entry("struct Qdisc",
                                             root.list.address_of_(), "list"):
                if qdisc.handle == major:
                    return qdisc

    return NULL(dev.prog_, "struct Qdisc *")
예제 #27
0
def list_for_each_spl_kmem_cache(prog: drgn.Program) -> Iterable[drgn.Object]:
    yield from list_for_each_entry("spl_kmem_cache_t",
                                   prog["spl_kmem_cache_list"].address_of_(),
                                   "skc_list")
예제 #28
0
파일: lsmod.py 프로젝트: shiloong/drgn
# Copyright (c) Facebook, Inc. and its affiliates.
# SPDX-License-Identifier: GPL-3.0+
"""An implementation of lsmod(8) using drgn"""

from drgn.helpers.linux.list import list_for_each_entry

print("Module                  Size  Used by")
for mod in list_for_each_entry("struct module", prog["modules"].address_of_(),
                               "list"):
    name = mod.name.string_().decode()
    size = (mod.init_layout.size + mod.core_layout.size).value_()
    refcnt = mod.refcnt.counter.value_() - 1
    print(f"{name:19} {size:>8}  {refcnt}", end="")
    first = True
    for use in list_for_each_entry("struct module_use",
                                   mod.source_list.address_of_(),
                                   "source_list"):
        if first:
            print(" ", end="")
            first = False
        else:
            print(",", end="")
        print(use.source.name.string_().decode(), end="")
    print()
예제 #29
0
def get_module_paths(osrelease: str, path: str) -> List[str]:
    """
    Use drgn on the crash dump specified by `path` and return list
    of paths from the `osrelease` kernel modules relevant to the
    crash dump.
    """
    #
    # Similarly to libkdumpfile we import these libraries locally
    # here so people who don't have drgn can still use savedump
    # for userland core dumps.
    #
    import drgn  # pylint: disable=import-outside-toplevel
    from drgn.helpers.linux.list import list_for_each_entry  # pylint: disable=import-outside-toplevel

    prog = drgn.program_from_core_dump(path)

    #
    # First go through all modules in the dump and create a map
    # of [key: module name] -> (value: module srcversion).
    #
    # Note:
    # It would be prefereable to be able to use the binary's
    # .build-id to do the matching instead of srcversion.
    # Unfortunately there doesn't seem to be a straightforward
    # way to get the build-id section of the ELF files recorded
    # in the dump. Hopefully that changes in the future.
    #
    mod_name_srcvers = {}
    for mod in list_for_each_entry('struct module',
                                   prog['modules'].address_of_(), 'list'):
        mod_name_srcvers[str(mod.name.string_(),
                             encoding='utf-8')] = str(mod.srcversion.string_(),
                                                      encoding='utf-8')

    #
    # Go through all modules in /usr/lib/debug/lib/modules/<osrelease>
    # and gather the file paths of the ones that are part of our
    # module name-to-srcversion map.
    #
    system_modules = pathlib.Path(
        f"/usr/lib/debug/lib/modules/{osrelease}/").rglob('*.ko')
    mod_paths = []
    for modpath in system_modules:
        modname = os.path.basename(modpath)[:-3]
        if not mod_name_srcvers.get(modname):
            continue

        success, output = shell_cmd(
            ['modinfo', '--field=srcversion',
             str(modpath)])
        if not success:
            sys.exit(output)
        output = output.strip()

        if output != mod_name_srcvers[modname]:
            continue

        mod_paths.append(str(modpath))
        del mod_name_srcvers[modname]

    print(f"found {len(mod_paths)} relevant modules with their debug info...")
    print("warning: could not find the debug info of the following modules:")
    print(f"  {', '.join(mod_name_srcvers.keys())}")
    return mod_paths
예제 #30
0
파일: fs_inodes.py 프로젝트: sravyamks/drgn
# Copyright (c) Facebook, Inc. and its affiliates.
# SPDX-License-Identifier: GPL-3.0+
"""List the paths of all inodes cached in a given filesystem"""

from drgn.helpers.linux.fs import for_each_mount, inode_path
from drgn.helpers.linux.list import list_for_each_entry
import os
import sys
import time

if len(sys.argv) == 1:
    path = "/"
else:
    path = sys.argv[1]

mnt = None
for mnt in for_each_mount(prog, dst=path):
    pass
if mnt is None:
    sys.exit(f"No filesystem mounted at {path}")

sb = mnt.mnt.mnt_sb

for inode in list_for_each_entry("struct inode", sb.s_inodes.address_of_(),
                                 "i_sb_list"):
    try:
        print(os.fsdecode(inode_path(inode)))
    except ValueError:
        continue