Example #1
0
def stats_command(args):
    """Implements the 'stats' command  for the 'wult' and 'ndl' tools."""

    if args.list_funcs:
        for name, descr in RORawResult.get_stat_funcs():
            _LOG.info("%s: %s", name, descr)
        return

    if args.funcs:
        funcnames = Trivial.split_csv_line(args.funcs)
        all_funcs = True
    else:
        funcnames = None
        all_funcs = False

    res = RORawResult.RORawResult(args.respath)
    apply_filters(args, res)

    non_numeric = res.get_non_numeric_colnames()
    if non_numeric and (args.csel or args.cfilt):
        non_numeric = ", ".join(non_numeric)
        _LOG.warning("skipping non-numeric column(s): %s", non_numeric)

    res.calc_stats(funcnames=funcnames, all_funcs=all_funcs)

    _LOG.info("Datapoints count: %d", len(res.df))
    YAML.dump(res.cstats, sys.stdout, float_format="%.2f")
Example #2
0
def report_command_open_raw_results(args):
    """
    Opens the input raw test results for the 'report' command of the 'wult' and 'ndl' tools. Returns
    the list of 'RORawResult' objects. At the same time, implements the '--list-columns' option by
    printing the column names for each input raw result.
    """

    if args.reportids:
        reportids = Trivial.split_csv_line(args.reportids)
    else:
        reportids = []

    if len(reportids) > len(args.respaths):
        raise Error(f"there are {len(reportids)} report IDs to assign to {len(args.respaths)} "
                    f"input test results. Please, provide {len(args.respaths)} or less report IDs.")

    # Append the required amount of 'None's to make the 'reportids' list be of the same length as
    # the 'args.respaths' list.
    reportids += [None] * (len(args.respaths) - len(reportids))

    rsts = []
    for respath, reportid in zip(args.respaths, reportids):
        if reportid:
            additional_chars = getattr(args, "reportid_additional_chars", None)
            ReportID.validate_reportid(reportid, additional_chars=additional_chars)

        res = RORawResult.RORawResult(respath, reportid=reportid)
        rsts.append(res)

        if args.list_columns:
            _LOG.info("Column names in '%s':", respath)
            for colname in res.colnames:
                _LOG.info("  * %s: %s", colname, res.defs.info[colname]["title"])

    return rsts
Example #3
0
def validate_ldist(ldist):
    """
    Validate and modify launch distanse. 'ldist' argument is string of single or two comma-separated
    integers, and tells launch distance in microseconds. Return value is launch distance in
    nanoseconds, as list of one or two integers.
    """

    ldst = Trivial.split_csv_line(ldist)

    for idx, val in enumerate(ldst):
        ldst[idx] = Trivial.str_to_num(val, default=None)
        if ldst[idx] is None:
            raise Error(f"bad launch distance '{ldist}', should be an integer")
        if ldst[idx] <= 0:
            raise Error(
                f"bad launch distance value '{ldst[idx]}', should be greater than zero"
            )

    ldst_str = ", ".join([str(val) for val in ldst])
    if len(ldst) > 2:
        raise Error(
            f"bad launch distance range '{ldst_str}', it should include 2 numbers"
        )
    if len(ldst) == 2 and ldst[1] - ldst[0] < 0:
        raise Error(
            f"bad launch distance range '{ldst_str}', first number cannot be "
            f"greater than the second number")

    # Return launch distance as nanoseconds.
    for idx, val in enumerate(ldst):
        ldst[idx] = val * 1000

    return ldst
Example #4
0
def _parse_ip_address_show(raw):
    """
    Parse output of the 'ip address show <IFNAME>' command and return the resulting dictionary.
    """

    info = {}
    for line in raw.splitlines():
        line = line.strip()
        elts = Trivial.split_csv_line(line, sep=" ")
        if re.match(r"^\d+:$", elts[0]):
            info["ifname"] = elts[1][:-1]
        elif elts[0] == "inet":
            ipnet = ipaddress.IPv4Network(elts[1], strict=False)
            info["ipv4"] = {}
            info["ipv4"]["ip"] = ipnet.network_address
            info["ipv4"]["mask"] = ipnet.netmask
            info["ipv4"]["bcast"] = ipnet.broadcast_address
            info["ipv4"]["ip_cidr"] = elts[1]
            info["ipv4"]["cidr"] = str(ipnet)
        elif elts[0] == "link/ether":
            info["ether"] = {}
            info["ether"]["mac"] = elts[1]
            info["ether"]["bcast"] = elts[3]

    return info
Example #5
0
    def do_filter(res, ops):
        """Apply filter operations in 'ops' to wult test result 'res'."""

        res.clear_filts()
        for name, expr in ops.items():
            if name.startswith("c"):
                expr = Trivial.split_csv_line(expr)
            getattr(res, f"set_{name}")(expr)
        res.load_df()
Example #6
0
def parse_int_list(nums, ints=False):
    """
    Turn a string contaning a comma-separated list of numbers and ranges into a list of numbers and
    return it. For example, a string like "0,1-3,7" would become ["0", "1", "2", "3", "7"]. The
    'ints' argument controls whether the resulting list should contain strings or integers.
    """

    if nums is None:
        return None

    if isinstance(nums, int):
        nums = str(nums)
    if isinstance(nums, str):
        nums = Trivial.split_csv_line(nums)

    nums_set = set()
    for elts in nums:
        elts = str(elts)
        if "-" in elts:
            elts = Trivial.split_csv_line(elts, sep="-")
            if len(elts) != 2:
                raise Error("bad range '%s', should be two integers separated by '-'" % elts)
        else:
            elts = [elts]

        for elt in elts:
            if not Trivial.is_int(elt):
                raise Error("bad number '%s', should be an integer" % elt)

        if len(elts) > 1:
            if int(elts[0]) > int(elts[1]):
                raise Error("bad range %s-%s, the first number should be smaller than thesecond"
                            % (elts[0], elts[1]))
            nums_set.update([str(elt) for elt in range(int(elts[0]), int(elts[1]) + 1)])
        else:
            nums_set.add(elts[0])

    result = sorted([int(num) for num in nums_set])
    if not ints:
        result = [str(num) for num in result]
    return result
Example #7
0
def _get_deployables(srcpath, proc):
    """
    Returns the list of "deployables" (driver names or helper tool names) provided by tools or
    drivers source code directory 'srcpath' on a host defined by 'proc'.
    """

    cmd = f"make --silent -C '{srcpath}' list_deployables"
    deployables, _ = proc.run_verify(cmd)
    if deployables:
        deployables = Trivial.split_csv_line(deployables, sep=" ")

    return deployables
Example #8
0
    def _build_colmap(self):
        """Build and return the turbostat -> CSV column names map."""

        self._colmap = {}

        # Build the turbostat -> CSV column names map.
        cmd = f"{self._ts_bin} -l"
        stdout, _ = self._proc.run_verify(cmd)
        for key in Trivial.split_csv_line(stdout):
            if key == "Busy%":
                self._colmap[key] = "CC0%"
            elif key.startswith("CPU%c"):
                self._colmap[key] = f"CC{key[5:]}%"
            elif key.startswith("Pkg%pc"):
                self._colmap[key] = f"PC{key[6:]}%"
Example #9
0
    def set_post_trigger(self, path, trange=None):
        """
        Configure the post-trigger - a program that has to be executed after a datapoint is
        collected. The arguments are as follows.
          * path - path to the executable program to run. The program will be executed with the
            '--latency <value>' option, where '<value>' is the observed wake latency value in
            nanoseconds.
          * trange - the post-trigger range. By default, the trigger program is executed on every
            datapoint. But if the trigger range is provided, the trigger program will be executed
            only when wake latency is in trigger range.
        """

        if not FSHelpers.isexe(path, proc=self._proc):
            raise Error(
                f"post-trigger program '{path}' does not exist{self._proc.hostmsg} or it "
                f"is not an executable file")

        self._post_trigger = path

        if trange is not None:
            vals = Trivial.split_csv_line(trange)

            for idx, val in enumerate(vals):
                if not Trivial.is_int(val):
                    raise Error(
                        f"bad post-trigger range value '{val}', should be an integer "
                        f"amount of nanoseconds")
                vals[idx] = Trivial.str_to_num(val, default=None)
                if vals[idx] < 0:
                    raise Error(
                        f"bad post trigger range value '{vals[idx]}', should be greater or "
                        f"equal to zero")

            if len(vals) != 2:
                raise Error(
                    f"bad post trigger range '{trange}', it should include 2 numbers"
                )
            if vals[1] - vals[0] < 0:
                raise Error(
                    f"bad post trigger range '{trange}', first number cannot be greater "
                    f"than the second number")

            self._post_trigger_range = vals
Example #10
0
    def _normalize_cstates(self, cstates):
        """
        Some methods accept the C-states to operate on as a string or a list. There may be C-state
        names or indexes in the list. This method turns the user input into a list of C-state
        indexes and returns this list.
        """

        if isinstance(cstates, int):
            cstates = str(cstates)
        if cstates is not None:
            if isinstance(cstates, str):
                cstates = Trivial.split_csv_line(cstates, dedup=True)
            indexes = []
            for cstate in cstates:
                if not Trivial.is_int(cstate):
                    cstate = self._name2idx(cstate)
                indexes.append(int(cstate))
            cstates = indexes
        return cstates
Example #11
0
    def _get_latency(self, dp):
        """
        Read the next latency data line from the 'ndlrunner' helper, parse it, and save the result
        in the 'dp' dictionary.
        """

        line = self._get_line(prefix="datapoint")
        line = Trivial.split_csv_line(line)

        if len(line) != 2:
            msg = self._unexpected_line_error_prefix(line)
            raise Error(
                f"{msg}\nExpected 2 comma-separated integers, got {len(line)}")

        for val in line:
            if not Trivial.is_int(val):
                msg = self._unexpected_line_error_prefix(line)
                raise Error(
                    f"{msg}\n: Expected 2 comma-separated integers, got a non-integer "
                    f"'{val}'")

        dp["RTD"] = int(line[0])
        dp["LDist"] = int(line[1])