Пример #1
0
def objs(cache: drgn.Object) -> int:
    assert sdb.type_canonical_name(cache.type_) == 'struct kmem_cache *'
    count: int = cache.node[0].total_objects.counter.value_()
    if is_root_cache(cache):
        for child in for_each_child_cache(cache):
            count += objs(child)
    return count
Пример #2
0
def for_each_slab_flag_in_cache(cache: drgn.Object) -> Iterable[str]:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    flag = cache.skc_flags.value_()
    for enum_entry, enum_entry_bit in cache.prog_.type(
            'enum kmc_bit').enumerators:
        if flag & (1 << enum_entry_bit):
            yield enum_entry.replace('_BIT', '')
Пример #3
0
def nr_slabs(cache: drgn.Object) -> int:
    assert sdb.type_canonical_name(cache.type_) == 'struct kmem_cache *'
    nslabs: int = cache.node[0].nr_slabs.counter.value_()
    if is_root_cache(cache):
        for child in for_each_child_cache(cache):
            nslabs += nr_slabs(child)
    return nslabs
Пример #4
0
    def _call(self, objs: Iterable[drgn.Object]) -> None:
        baked = {
            sdb.type_canonicalize_name(type_): class_
            for type_, class_ in sdb.PrettyPrinter.all_printers.items()
        }

        handling_class = None
        first_obj_type, objs = sdb.get_first_type(objs)
        if first_obj_type is not None:
            first_obj_type_name = sdb.type_canonical_name(first_obj_type)
            if first_obj_type_name in baked:
                handling_class = baked[first_obj_type_name]

        if handling_class is None:
            if first_obj_type is not None:
                msg = 'could not find pretty-printer for type {}\n'.format(
                    first_obj_type)
            else:
                msg = 'could not find pretty-printer\n'
            msg += "The following types have pretty-printers:\n"
            msg += f"\t{'PRINTER':<20s} {'TYPE':<20s}\n"
            for type_name, class_ in sdb.PrettyPrinter.all_printers.items():
                msg += f"\t{class_.names[0]:<20s} {type_name:<20s}\n"
            raise sdb.CommandError(self.name, msg)

        handling_class().pretty_print(objs)
Пример #5
0
def entry_size(cache: drgn.Object) -> int:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    if backed_by_linux_cache(cache):
        return slub.entry_size(cache.skc_linux_cache)
    ops = objs_per_slab(cache)
    if ops == 0:
        return 0
    return int(slab_size(cache) / objs_per_slab(cache))
Пример #6
0
def slab_linux_cache_source(cache: drgn.Object) -> str:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    if not backed_by_linux_cache(cache):
        name = slab_name(cache)
        subsystem = "SPL"
    else:
        name = cache.skc_linux_cache.name.string_().decode('utf-8')
        subsystem = "SLUB"
    return f"{name}[{subsystem:4}]"
Пример #7
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
Пример #8
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)
Пример #9
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)
Пример #10
0
def cache_get_free_pointer(cache: drgn.Object, p: drgn.Object) -> drgn.Object:
    """
    Get the next pointer in the freelist. Note, that this
    function assumes that CONFIG_SLAB_FREELIST_HARDENED
    is set in the target
    """
    assert sdb.type_canonical_name(cache.type_) == 'struct kmem_cache *'
    assert sdb.type_canonical_name(p.type_) == 'void *'
    hardened_ptr = p + cache.offset.value_()

    #
    # We basically do what `freelist_dereference()` and
    # `freelist_ptr()` do in the kernel source:
    #
    # ptr <- (void *)*(unsigned long *)(hardened_ptr)
    #
    intermediate_ulong = drgn.Object(sdb.get_prog(),
                                     type='unsigned long',
                                     address=hardened_ptr.value_())
    ptr = drgn.Object(sdb.get_prog(),
                      type='void *',
                      value=intermediate_ulong.value_())

    #
    # ptr_addr <- (unsigned long)hardened_ptr
    #
    ptr_addr = drgn.Object(sdb.get_prog(),
                           type='unsigned long',
                           value=hardened_ptr.value_())

    #
    # return (void *)((unsigned long)ptr ^ cache->random ^ ptr_addr)
    #
    ptr_as_ulong = drgn.Object(sdb.get_prog(),
                               type='unsigned long',
                               value=ptr.value_())
    clean_ptr_val = ptr_as_ulong.value_()
    clean_ptr_val ^= cache.random.value_()
    clean_ptr_val ^= ptr_addr.value_()
    return drgn.Object(sdb.get_prog(), type='void *', value=clean_ptr_val)
Пример #11
0
def obj_alloc(cache: drgn.Object) -> int:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    if backed_by_linux_cache(cache):
        try:
            return int(drgn_percpu.percpu_counter_sum(cache.skc_linux_alloc))
        except AttributeError:
            #
            # The percpu_counter referenced above wasn't in ZoL until the
            # following commit: ec1fea4516ac2f0c08d31d6308929298d1b281d0
            #
            # Fall back to the old-mechanism of using skc_obj_alloc if that
            # percpu_counter member doesn't exist (an AttributeError will
            # be thrown).
            #
            pass
    return int(cache.skc_obj_alloc.value_())
Пример #12
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)
Пример #13
0
def for_each_onslab_object_in_slab(slab: drgn.Object) -> Iterable[drgn.Object]:
    assert sdb.type_canonical_name(slab.type_) == 'struct spl_kmem_slab *'
    cache = slab.sks_cache
    sks_size = spl_aligned_slab_size(cache)
    spl_obj_size = spl_aligned_obj_size(cache)

    for i in range(slab.sks_objs.value_()):
        obj = sdb.create_object('void *',
                                slab.value_() + sks_size + (i * spl_obj_size))
        #
        # If the sko_list of the object is empty, it means that
        # this object is not part of the slab's internal free list
        # and therefore it is allocated. NOTE: sko_list in the
        # actual code is not a list, but a link on a list. Thus,
        # the check below is not checking whether the "object
        # list" is empty for this slab, but rather whether the
        # link is part of any list.
        #
        sko = sko_from_obj(cache, obj)
        assert sko.sko_magic.value_() == 0x20202020  # SKO_MAGIC
        if linked_lists.is_list_empty(sko.sko_list):
            yield obj
Пример #14
0
def object_size(cache: drgn.Object) -> int:
    assert sdb.type_canonical_name(cache.type_) == 'struct kmem_cache *'
    return int(cache.object_size.value_())
Пример #15
0
def slab_size(cache: drgn.Object) -> int:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    return int(cache.skc_slab_size.value_())
Пример #16
0
def slab_name(cache: drgn.Object) -> str:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    return str(cache.skc_name.string_().decode('utf-8'))
Пример #17
0
def backed_by_linux_cache(cache: drgn.Object) -> bool:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    return int(cache.skc_linux_cache.value_()) != 0x0
Пример #18
0
def sko_from_obj(cache: drgn.Object, obj: drgn.Object) -> drgn.Object:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    cache_obj_align = cache.skc_obj_align.value_()
    return sdb.create_object(
        'spl_kmem_obj_t *',
        obj.value_() + p2.p2roundup(object_size(cache), cache_obj_align))
Пример #19
0
def spl_aligned_slab_size(cache: drgn.Object) -> int:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    cache_obj_align = cache.skc_obj_align.value_()
    spl_slab_type_size = sdb.type_canonicalize_size('spl_kmem_slab_t')
    return p2.p2roundup(spl_slab_type_size, cache_obj_align)
Пример #20
0
def total_memory(cache: drgn.Object) -> int:
    assert sdb.type_canonical_name(cache.type_) == 'struct kmem_cache *'
    nslabs = nr_slabs(cache)
    epslab = entries_per_slab(cache)
    esize = entry_size(cache)
    return nslabs * epslab * esize
Пример #21
0
def is_list_empty(l: drgn.Object) -> bool:
    """
    True if list is empty, False otherwise.
    """
    assert sdb.type_canonical_name(l.type_) == 'struct list_head'
    return int(l.address_of_().value_()) == int(l.next.value_())
Пример #22
0
def nr_objects(cache: drgn.Object) -> int:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    if backed_by_linux_cache(cache):
        return int(cache.skc_obj_alloc.value_())
    return int(cache.skc_obj_total.value_())
Пример #23
0
def active_objs(cache: drgn.Object) -> int:
    assert sdb.type_canonical_name(cache.type_) == 'struct kmem_cache *'
    return objs(cache) - inactive_objs(cache)
Пример #24
0
def obj_inactive(cache: drgn.Object) -> int:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    return nr_objects(cache) - obj_alloc(cache)
Пример #25
0
def slab_flags(cache: drgn.Object) -> str:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    return '|'.join(for_each_slab_flag_in_cache(cache))
Пример #26
0
def active_memory(cache: drgn.Object) -> int:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    return obj_alloc(cache) * entry_size(cache)
Пример #27
0
def obj_alloc(cache: drgn.Object) -> int:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    return int(cache.skc_obj_alloc.value_())
Пример #28
0
def total_memory(cache: drgn.Object) -> int:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    if backed_by_linux_cache(cache):
        return slub.total_memory(cache.skc_linux_cache)
    return slab_size(cache) * nr_slabs(cache)
Пример #29
0
def get_valid_type_by_name(cmd: sdb.Command, tname: str) -> drgn.Type:
    """
    Given a type name in string form (`tname`) without any C keyword
    prefixes (e.g. 'struct', 'enum', 'class', 'union'), return the
    corresponding drgn.Type object.

    This function is used primarily by commands that accept a type
    name as an argument and exist only to save keystrokes for the
    user.
    """
    if tname in ['struct', 'enum', 'union', 'class']:
        #
        # Note: We have to do this because currently in drgn
        # prog.type('struct') returns a different error than
        # prog.type('bogus'). The former returns a SyntaxError
        # "null identifier" while the latter returns LookupError
        # "could not find typedef bogus". The former is not
        # user-friendly and thus we just avoid that situation
        # by instructing the user to skip such keywords.
        #
        raise sdb.CommandError(
            cmd.name,
            f"skip keyword '{tname}' or quote your type \"{tname} <typename>\"")

    try:
        type_ = sdb.get_type(tname)
        if type_.kind == drgn.TypeKind.TYPEDEF and type_.type_name(
        ) == sdb.type_canonical_name(type_):
            #
            # In some C codebases there are typedefs like this:
            #
            #     typedef union GCObject GCObject; // taken from LUA repo
            #
            # The point of the above is to avoid typing the word
            # 'union' every time we declare a variable of that type.
            # For the purposes of SDB, passing around a drng.Type
            # describing the typedef above isn't particularly
            # useful. Using such an object with the `ptype` command
            # (one of the consumers of this function) would yield
            # the following:
            #
            #     sdb> ptype GCObject
            #     typedef union GCObject GCObject
            #
            # Resolving the typedef's explicitly in those cases
            # is more useful and this is why this if-clause exists.
            #
            #     sdb> ptype GCObject
            #     union GCObject {
            #             GCheader gch;
            #             union TString ts;
            #             ...
            #     }
            #
            return sdb.type_canonicalize(type_)
        return type_
    except LookupError:
        #
        # We couldn't find a type with that name. Check if
        # it is a structure, an enum, or a union.
        #
        pass
    for prefix in ["struct ", "enum ", "union "]:
        try:
            return sdb.get_type(f"{prefix}{tname}")
        except LookupError:
            pass
    raise sdb.CommandError(
        cmd.name,
        f"couldn't find typedef, struct, enum, nor union named '{tname}'")
Пример #30
0
def util(cache: drgn.Object) -> int:
    assert sdb.type_canonical_name(cache.type_) == 'struct spl_kmem_cache *'
    total_mem = total_memory(cache)
    if total_mem == 0:
        return 0
    return int((active_memory(cache) / total_mem) * 100)