예제 #1
0
파일: histograms.py 프로젝트: sravyamks/sdb
    def print_histogram(hist: drgn.Object,
                        offset: int = 0,
                        indent: int = 0) -> None:
        canonical_type = sdb.type_canonicalize(hist.type_)
        assert canonical_type.kind == drgn.TypeKind.ARRAY
        assert sdb.type_canonicalize(
            canonical_type.type).kind == drgn.TypeKind.INT

        max_count = 0
        min_bucket = (len(hist) - 1)
        max_bucket = 0
        for (bucket, value) in enumerate(hist):
            count = int(value)
            if bucket < min_bucket and count > 0:
                min_bucket = bucket
            if bucket > max_bucket and count > 0:
                max_bucket = bucket
            if count > max_count:
                max_count = count

        HISTOGRAM_WIDTH_MAX = 40
        if max_count < HISTOGRAM_WIDTH_MAX:
            max_count = HISTOGRAM_WIDTH_MAX

        for bucket in range(min_bucket, max_bucket + 1):
            count = int(hist[bucket])
            stars = round(count * HISTOGRAM_WIDTH_MAX / max_count)
            print(f'{" " * indent}{fmt.size_nicenum(2**(bucket+offset)):>8}: '
                  f'{count:>6} {"*" * stars}')
예제 #2
0
    def histogram_median(hist: drgn.Object, offset: int = 0) -> int:
        """
        Returns the approximated median of a ZFS histogram.
        """
        canonical_type = sdb.type_canonicalize(hist.type_)
        assert canonical_type.kind == drgn.TypeKind.ARRAY
        assert sdb.type_canonicalize(
            canonical_type.type).kind == drgn.TypeKind.INT

        total_space = 0
        for (bucket, value) in enumerate(hist):
            total_space += int(value) << (bucket + offset)

        if total_space == 0:
            return 0

        space_left, median = total_space / 2, 0
        for (bucket, value) in enumerate(hist):
            space_in_bucket = int(value) << (bucket + offset)
            if space_left <= space_in_bucket:
                median = 1 << (bucket + offset - 1)
                #
                # Size of segments may vary within one bucket thus we
                # attempt to approximate the median by looking at the
                # number of segments in the bucket and assuming that
                # they are evenly distributed along the bucket's range.
                #
                bucket_fill = space_left / space_in_bucket
                median += round(median * bucket_fill)
                break
            space_left -= space_in_bucket
        return median
예제 #3
0
파일: array.py 프로젝트: sravyamks/sdb
    def _call_one(self, obj: drgn.Object) -> Iterable[drgn.Object]:
        nelems = 0

        obj_type = sdb.type_canonicalize(obj.type_)
        if obj_type.kind == drgn.TypeKind.ARRAY:
            array_elems = len(obj)
            if self.args.nelems is not None:
                nelems = self.args.nelems
                if nelems > array_elems:
                    print(
                        f"warning: requested size {nelems} exceeds type size {array_elems}"
                    )
            else:
                nelems = array_elems

        elif obj_type.kind == drgn.TypeKind.POINTER:
            if self.args.nelems is None:
                err_msg = (f"'{obj.type_.type_name()}' is a pointer - "
                           "please specify the number of elements")
                raise sdb.CommandError(self.name, err_msg)
            nelems = self.args.nelems

            if not obj_type.type.is_complete():
                err_msg = ("can't walk pointer array of incomplete type "
                           f"'{obj_type.type.type_name()}'")
                raise sdb.CommandError(self.name, err_msg)
        else:
            raise sdb.CommandError(
                self.name,
                f"'{obj.type_.type_name()}' is not an array nor a pointer type")

        for i in range(nelems):
            yield obj[i]
예제 #4
0
 def _call(self, objs: Iterable[drgn.Object]) -> Iterable[drgn.Object]:
     result = 0
     for obj in objs:
         type_ = sdb.type_canonicalize(obj.type_)
         if type_.kind != drgn.TypeKind.INT and type_.kind != drgn.TypeKind.POINTER:
             raise sdb.CommandError(
                 self.name, f"'{type_.type_name()}' is not an integer type")
         result += int(obj.value_())
     yield sdb.create_object('uint64_t', result)
예제 #5
0
    def _validate_type_dereference(self, obj: drgn.Object,
                                   sep: MemberExprSep) -> None:
        """
        The conventions when dereferencing structure members and array
        elements come straight from the standard C notation:
        [1] The dot notation/operator `.` can be used to reference
            members in an embedded struct.
        [2] `->` can be used to walk/dereference struct members that
            are pointers.
        [3] Array notation can be used to look into a specific element
            of an array using the index notation. This include both
            formally specified arrays (like char[100]) and pointers
            that are used as arrays allocated at runtime.

        Something worth noting from a usability perspective for struct
        members that are pointers (see [2] above) is that SDB relaxes
        the notation and allows dereference with the `.` operator too.
        This means that something like this:
        `... | member ptr0->ptr1->ptr2`
        could be also written like this:
        `... | member ptr0.ptr1.ptr2`
        Besides saving one keystroke, this improves usability as users
        don't have to care about the exact type of the member being a
        struct or a pointer to a struct. This should also help users
        that are switching between DRGN and SDB during development and
        people coming from Acid.
        """
        kind = sdb.type_canonicalize(obj.type_).kind

        # This is the first term, no need to do validation.
        if sep == MemberExprSep.START:
            return

        if kind == drgn.TypeKind.STRUCT and sep != MemberExprSep.DOT:
            raise sdb.CommandError(
                self.name,
                "'{}' is a struct - use the dot(.) notation for member access".
                format(obj.type_))

        if kind == drgn.TypeKind.POINTER:
            assert sep in [
                MemberExprSep.PTR, MemberExprSep.DOT, MemberExprSep.ARRAY
            ]
            return

        if kind == drgn.TypeKind.ARRAY and sep != MemberExprSep.ARRAY:
            raise sdb.CommandError(
                self.name,
                "'{}' is an array - cannot use '{}' notation".format(
                    obj.type_, sep.value))
예제 #6
0
    def print_histogram(hist: drgn.Object,
                        offset: int = 0,
                        indent: int = 0) -> None:
        canonical_type = sdb.type_canonicalize(hist.type_)
        assert canonical_type.kind == drgn.TypeKind.ARRAY
        assert sdb.type_canonicalize(
            canonical_type.type).kind == drgn.TypeKind.INT

        max_count = 0
        min_bucket = (len(hist) - 1)
        max_bucket = 0
        for (bucket, value) in enumerate(hist):
            count = int(value)
            if bucket < min_bucket and count > 0:
                min_bucket = bucket
            if bucket > max_bucket and count > 0:
                max_bucket = bucket
            if count > max_count:
                max_count = count

        HISTOGRAM_WIDTH_MAX = 40
        max_count = max(max_count, HISTOGRAM_WIDTH_MAX)

        if min_bucket > max_bucket:
            print(f'{" " * indent}** No histogram data available **')
            return

        print(f'{" " * indent}seg-size   count')
        print(f'{" " * indent}{"-" * 8}   {"-" * 5}')

        for bucket in range(min_bucket, max_bucket + 1):
            count = int(hist[bucket])
            stars = round(count * HISTOGRAM_WIDTH_MAX / max_count)
            print(f'{" " * indent}{fmt.size_nicenum(2**(bucket+offset)):>8}: '
                  f'{count:>6} {"*" * stars}')

        ZFSHistogram.print_histogram_median(hist, offset, indent)
예제 #7
0
def get_valid_struct_name(cmd: sdb.Command, tname: str) -> str:
    """
    If tname is a name of a type that's a typedef to a
    struct, this function return will it as is. If this
    is a name of a struct, then it returns the canonical
    name (e.g. adds "struct" prefix). Otherwise, raises
    an error.

    Used for shorthands in providing names of structure
    types to be consumed by drgn interfaces in string
    form (linux_list, container_of, etc..).
    """
    if tname in ['struct', '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}' and try again")

    try:
        type_ = sdb.get_prog().type(tname)
    except LookupError:
        # Check for struct
        struct_name = f"struct {tname}"
        try:
            type_ = sdb.get_prog().type(struct_name)
        except LookupError as err:
            raise sdb.CommandError(cmd.name, str(err))
        return struct_name

    # Check for typedef to struct
    if type_.kind != drgn.TypeKind.TYPEDEF:
        raise sdb.CommandError(
            cmd.name, f"{tname} is not a struct nor a typedef to a struct")
    if sdb.type_canonicalize(type_).kind != drgn.TypeKind.STRUCT:
        raise sdb.CommandError(cmd.name,
                               f"{tname} is not a typedef to a struct")
    return tname
예제 #8
0
def get_valid_struct_name(cmd: sdb.Command, tname: str) -> str:
    """
    If tname is a name of a type that's a typedef to a
    struct, this function return will it as is. If this
    is a name of a struct, then it returns the canonical
    name (e.g. adds "struct" prefix). Otherwise, raises
    an error.

    Used for shorthands in providing names of structure
    types to be consumed by drgn interfaces in string
    form (linux_list, container_of, etc..).
    """
    type_ = get_valid_type_by_name(cmd, tname)

    if type_.kind == drgn.TypeKind.STRUCT:
        return str(type_.type_name())

    # canonicalize in case this is a typedef to a struct
    canonical_type_ = sdb.type_canonicalize(type_)
    if canonical_type_.kind == drgn.TypeKind.STRUCT:
        return str(canonical_type_.type_name())

    raise sdb.CommandError(
        cmd.name, f"{tname} is not a struct nor a typedef to a struct")
예제 #9
0
    def _validate_array_index(self, type_: drgn.Type, idx: int) -> None:
        """
        Array index validation is a lot weaker than the generic type
        validation because of zero-length arrays and pretty-much any
        kind of array allocated on the heap at runtime.

        If we are using the array notation with a pointer, no validation
        happens whatsoever because we can't tell if we are out of bounds
        for something that was allocated at runtime.

        If we are using the array notation on a zero-length array we
        still have the above problem, even though the type comes with
        in size (generally 0 or 1 elements). Because of this reason
        when we encounter an index that is out-of-bounds we print a
        warning and move on instead of raising an error.
        """
        base_kind = sdb.type_canonicalize(type_).kind

        if base_kind == drgn.TypeKind.POINTER:
            return
        assert base_kind == drgn.TypeKind.ARRAY
        if type_.length <= idx:
            warn_msg = f"index out of bounds for array of type '{type_}' (requested index: {idx})"
            print(f"warning: {self.name}: {warn_msg}")
예제 #10
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}'")
예제 #11
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. If `tname` starts with a C keyword
    we just return the type as is.

    This function is used primarily by commands that accept a type
    name as an argument and exist only to save keystrokes for the
    user.
    """
    TYPE_KEYWORDS = ['struct', 'enum', 'union', 'class']

    tokens = tname.split()
    if len(tokens) > 2:
        #
        # drgn fails in all kinds of ways when we pass it an
        # invalid type that consists of more than 2 text tokens.
        #
        raise sdb.CommandError(cmd.name,
                               f"input '{tname}' is not a valid type name")

    if len(tokens) == 2:
        if tokens[0] not in TYPE_KEYWORDS or tokens[1] in TYPE_KEYWORDS:
            #
            # For the same reason mentioned in the above comment
            # we also ensure that someone may not invalid two-token
            # input that has the following errors:
            # 1] Doesn't start with a type keyword - e.g "bogus type"
            # 2] Has a type keyword as its type name (also see
            #    comment below) - e.g. struct struct
            #
            raise sdb.CommandError(
                cmd.name, f"input '{tname}' is not a valid type name")
        try:
            return sdb.get_type(tname)
        except LookupError as err:
            raise sdb.CommandError(cmd.name,
                                   f"couldn't find type '{tname}'") from err
        except SyntaxError as err:
            raise sdb.CommandError(
                cmd.name, f"input '{tname}' is not a valid type name") from err

    if tname in TYPE_KEYWORDS:
        #
        # 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
    except SyntaxError as err:
        raise sdb.CommandError(
            cmd.name, f"input '{tname}' is not a valid type name") from err
    for prefix in TYPE_KEYWORDS:
        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}'")