Beispiel #1
0
    def _copy_raw_data(self):
        """Copy raw test results to the output directory."""

        # Paths to the raw statistics.
        raw_stats_paths = {}
        # Paths to test reports' description files.
        descr_paths = {}

        for res in self.rsts:
            srcpath = res.dirpath
            resrootdir = "raw-" + res.reportid
            dstpath = self.outdir.joinpath(resrootdir)

            if self.relocatable:
                action = "copy"
            else:
                action = "symlink"
            FSHelpers.move_copy_link(srcpath,
                                     dstpath,
                                     action=action,
                                     exist_ok=True)

            if res.stats_path.is_dir():
                hlink = f"<a href=\"{resrootdir}/{res.stats_path.name}\">Raw statistics</a>"
                raw_stats_paths[res.reportid] = hlink
            if res.descr_path.is_file():
                hlink = f"<a href=\"{resrootdir}/{res.descr_path.name}\">Test description</a>"
                descr_paths[res.reportid] = hlink

        return raw_stats_paths, descr_paths
Beispiel #2
0
    def __init__(self, proc, timeout=30):
        """
        Class constructor. The arguments are as follows.
          * proc - the 'Proc' or 'SSH' object that defines the host to operate on. This object will
                   keep a 'proc' reference and use it in various methods.
          * timeout - longest time in seconts to wait for data in the trace buffer.
        """

        self._reader = None
        self._proc = proc
        self.timeout = timeout
        self.raw_line = None

        mntpoint = FSHelpers.mount_debugfs(proc=proc)
        self.ftpath = mntpoint.joinpath("tracing/trace")
        self.ftpipe_path = mntpoint.joinpath("tracing/trace_pipe")

        for path in (self.ftpath, self.ftpipe_path):
            if not FSHelpers.isfile(path, proc=proc):
                raise ErrorNotSupported(
                    f"linux kernel function trace file was not found at "
                    f"'{path}'{proc.hostmsg}")

        cmd = f"cat {self.ftpipe_path}"
        name = "stale wult function trace reader process"
        ProcHelpers.kill_processes(cmd, log=True, name=name, proc=self._proc)
        self._clear()
        self._reader = self._proc.run_async(cmd, shell=True)
Beispiel #3
0
    def _generate_report(self):
        """Put together the final HTML report."""

        _LOG.info("Generating the HTML report.")

        # Make sure the output directory exists.
        try:
            self.outdir.mkdir(parents=True, exist_ok=True)
        except OSError as err:
            raise Error(f"failed to create directory '{self.outdir}': {err}")

        raw_stats_paths, descr_paths = self._copy_raw_data()

        # Find the styles and templates paths.
        templdir = FSHelpers.search_for_app_data(
            "wult",
            Path("templates"),
            pathdescr="HTML report Jinja2 templates")
        csspath = FSHelpers.search_for_app_data(
            "wult", Path("css/style.css"), pathdescr="HTML report CSS file")

        # Copy the styles file to the output directory.
        dstpath = self.outdir.joinpath("style.css")
        try:
            shutil.copyfile(csspath, dstpath)
        except OSError as err:
            raise Error(
                f"failed to copy CSS file from '{csspath}' to '{dstpath}':\n{err}"
            )

        # The summary table is only included into the main HTML page.
        sum_tbl = self._prepare_summary_table(raw_stats_paths, descr_paths)
        links_tbl = self._prepare_links_table()

        # Each column name gets its own HTML page.
        for colname, pinfos in self._pinfos.items():
            stats_tbl = self._prepare_stats_table(pinfos)

            # Render the template.
            jenv = Jinja2.build_jenv(templdir,
                                     trim_blocks=True,
                                     lstrip_blocks=True)
            jenv.globals["stats_tbl"] = stats_tbl
            jenv.globals["pinfos"] = pinfos
            jenv.globals["colname"] = colname
            jenv.globals["title_descr"] = self.title_descr
            jenv.globals["toolname"] = self._refinfo["toolname"]

            if sum_tbl:
                jenv.globals["sum_tbl"] = sum_tbl
                jenv.globals["links_tbl"] = links_tbl
                templfile = outfile = "index.html"
                sum_tbl = None
            else:
                templfile = "metric.html"
                outfile = links_tbl[colname]["fname"]

            Jinja2.render_template(jenv,
                                   Path(templfile),
                                   outfile=self.outdir.joinpath(outfile))
Beispiel #4
0
    def __init__(self, devid, cpunum, proc):
        """The class constructor. The arguments are the same as in '_WultDeviceBase.__init__()'."""

        super().__init__(devid, cpunum, proc)

        self._pci_info = None
        self._devpath = None
        self._captured = None

        path = Path(f"/sys/bus/pci/devices/{self._devid}")
        if not FSHelpers.exists(path, proc=proc):
            raise ErrorNotFound(f"cannot find device '{self._devid}'{self._proc.hostmsg}:\n"
                                f"path {path} does not exist")

        self._devpath = FSHelpers.abspath(path, proc=self._proc)
        self._pci_info = LsPCI.LsPCI(proc).get_info(Path(self._devpath).name)

        if self.supported_devices and self._pci_info['devid'] not in self.supported_devices:
            supported = ["%s - %s" % (key, val) for key, val in self.supported_devices.items()]
            supported = "\n * ".join(supported)
            raise ErrorNotSupported(f"PCI device '{self._pci_info['pciaddr']}' (PCI ID "
                                    f"{self._pci_info['devid']}) is not supported by wult driver "
                                    f"{self.drvname}.\nHere is the list of supported PCI IDs:\n* "
                                    f"{supported}")

        self.info["name"] = "Intel I210"
        self.info["devid"] = self._pci_info["pciaddr"]
        if self.supported_devices:
            self.info["descr"] = self.supported_devices[self._pci_info['devid']]
        else:
            self.info["name"] = self._pci_info["name"]
            self.info["descr"] = self.info['name'].capitalize()

        self.info["descr"] += f". PCI address {self._pci_info['pciaddr']}, Vendor ID " \
                              f"{self._pci_info['vendorid']}, Device ID {self._pci_info['devid']}."
Beispiel #5
0
    def _get_driver(self):
        """
        Find out whether the PCI device is bound to any driver. If it is not, returns the
        '(None, None)' tuple. Otherwise returns a tuple of:
         * driver name
         * driver sysfs path
        """

        drvpath = Path(f"{self._devpath}/driver")
        if not FSHelpers.exists(drvpath, proc=self._proc):
            return (None, None)

        drvpath = FSHelpers.abspath(drvpath, proc=self._proc)
        drvname = Path(drvpath).name
        return (drvname, drvpath)
Beispiel #6
0
    def _get_hw_addr(self):
        """
        Return the hardware address for the NIC corresponding to the network interface. Typically
        the hardware address is a PCI address, such as '0000:04:00.0'.
        """

        # The "device" symlink leads to the sysfs subdirectory corresponding to the underlying NIC.
        path = self._sysfsbase / "device"
        if not FSHelpers.exists(path, proc=self._proc):
            raise ErrorNotFound(
                f"cannot find network interface '{self.ifname}':\n"
                f"path '{path}' does not exist{self._proc.hostmsg}'")

        # The name of the subdirectory is the hardware address.
        path = FSHelpers.abspath(path, proc=self._proc)
        return path.name
Beispiel #7
0
    def get_path_pairs(proc, toolname, helperpath):
        """
        Yield paths for 'toolname' driver and helpertool source code and deployables. Arguments are
        same as in 'is_deploy_needed()'.
        """

        lproc = Procs.Proc()

        for path, is_drv in [(_DRV_SRC_SUBPATH, True), (_HELPERS_SRC_SUBPATH, False)]:
            srcpath = FSHelpers.search_for_app_data(toolname, path / toolname, default=None)
            # Some tools may not have helpers.
            if not srcpath:
                continue

            for deployable in _get_deployables(srcpath, lproc):
                deploypath = None
                # Deployable can be driver module or helpertool.
                if is_drv:
                    deploypath = _get_module_path(proc, deployable)
                else:
                    if helperpath and helperpath.name == deployable:
                        deploypath = helperpath
                    else:
                        deploypath = get_helpers_deploy_path(proc, toolname)
                        deploypath = Path(deploypath, "bin", deployable)
                yield srcpath, deploypath
Beispiel #8
0
    def _set_launch_distance(self):
        """Set launch distance limits to driver."""

        try:
            limit_path = self._basedir / "ldist_max_nsec"
            with self._proc.open(limit_path, "r") as fobj:
                ldist_max = fobj.read().strip()

            limit_path = self._basedir / "ldist_min_nsec"
            with self._proc.open(limit_path, "r") as fobj:
                ldist_min = fobj.read().strip()
        except Error as err:
            raise Error(
                f"failed to read launch distance limit from '{limit_path}'"
                f"{self._proc.hostmsg}:\n{err}")

        ldist_min = Trivial.str_to_num(ldist_min)
        ldist_max = Trivial.str_to_num(ldist_max)
        from_path = self._basedir / "ldist_from_nsec"
        to_path = self._basedir / "ldist_to_nsec"

        for ldist, ldist_path in zip_longest(self._ldist, [from_path, to_path],
                                             fillvalue=self._ldist[-1]):
            if ldist < ldist_min or ldist > ldist_max:
                raise Error(
                    f"launch distance '{ldist}' is out of range, it should be in range of "
                    f"[{ldist_min},{ldist_max}]")
            if not FSHelpers.exists(ldist_path, proc=self._proc):
                raise Error(
                    f"path i'{ldist_path}' does not exist{self._proc.hostmsg}")
            # open()/write() doesn't work for this file when done over SSH.
            self._proc.run_verify(f"echo {ldist} > {ldist_path}", shell=True)
Beispiel #9
0
    def disable_ipv6(self):
        """Disable IPv6 for the network interface."""

        path = Path("/proc/sys/net/ipv6/conf/{self.ifname}/disable_ipv6")
        if FSHelpers.isfile(path, proc=self._proc):
            with self._proc.open(path, "w") as fobj:
                fobj.write("1")
Beispiel #10
0
def get_helpers_deploy_path(proc, toolname):
    """
    Get helpers deployment path for 'toolname' on the system associated with the 'proc' object.
    """

    helpers_path = os.environ.get(f"{toolname.upper()}_HELPERSPATH")
    if not helpers_path:
        helpers_path = FSHelpers.get_homedir(proc=proc) / HELPERS_LOCAL_DIR
    return helpers_path
Beispiel #11
0
    def start(self):
        """Start the latency measurements."""

        # Sanity check.
        if not FSHelpers.exists(self._enable_path, proc=self._proc):
            raise Error(
                f"path {self._enable_path} does not exist{self._proc.hostmsg}")

        self._proc.run_verify(f"echo 1 > {self._enable_path}", shell=True)
Beispiel #12
0
    def bind(self, drvname):
        """Bind the PCI device to driver 'drvname'."""

        _LOG.debug("binding device '%s' to driver '%s'%s",
                   self._pci_info["pciaddr"], drvname, self._proc.hostmsg)

        failmsg = f"failed to bind device '{self._pci_info['pciaddr']}' to driver '{drvname}'" \
                  f"{self._proc.hostmsg}"

        self._dmesg_capture()

        drvpath = Path(f"/sys/bus/pci/drivers/{drvname}")
        if not FSHelpers.exists(drvpath, proc=self._proc):
            raise Error(f"{failmsg}':\npath '{drvpath}' does not exist{self._proc.hostmsg}")

        cur_drvname = self.get_driver_name()
        if cur_drvname == drvname:
            _LOG.debug("device '%s' is already bound to driver '%s'%s",
                       self._pci_info["pciaddr"], drvname, self._proc.hostmsg)
            return

        if cur_drvname:
            raise Error(f"{failmsg}:\nit is already bound to driver '{cur_drvname}'")

        # At this point we do not know if the driver supports this PCI ID. So start with the
        # assumption that it does not, in which case writing to the 'new_id' file should do both:
        # * make the driver aware of the PCI ID
        # * bind the device
        path = f"{drvpath}/new_id"
        val = f"{self._pci_info['vendorid']} {self._pci_info['devid']}"
        bound = True

        try:
            with self._proc.open(path, "wt") as fobj:
                _LOG.debug("writing '%s' to file '%s'", val, path)
                fobj.write(val)
        except Error as err:
            bound = False

        if not bound:
            # Probably the driver already knows about this PCI ID. Use the 'bind' file in this case.
            path = f"{drvpath}/bind"
            val = self._pci_info["pciaddr"]
            with self._proc.open(path, "wt") as fobj:
                _LOG.debug("writing '%s' to file '%s'", val, path)
                try:
                    fobj.write(val)
                except Error as err:
                    raise Error(f"{failmsg}:\n{err}\n{self._get_new_dmesg()}")

        # Verify that the device is bound to the driver.
        if not self._get_driver()[1]:
            raise Error(f"{failmsg}\n{self._get_new_dmesg()}")

        _LOG.debug("binded device '%s' to driver '%s'%s\n%s", self._pci_info["pciaddr"], drvname,
                   self._proc.hostmsg, self._get_new_dmesg())
Beispiel #13
0
    def __init__(self,
                 ifname,
                 tc_bin="tc",
                 handover_delta=500000,
                 phc2sys_bin="phc2sys",
                 proc=None):
        """
        Class constructor. The arguments are as follows.
          * ifname - name of the interface to use for scheduling network packets in the future.
          * tc_bin - path to the 'tc' tool that should be used for setting up the ETF qdisc.
          * handover_delta - the qdisc delta - the time offset in microseconds when the qdisc hands
                             the packet over to the network driver.
          * phc2sys_bin - path to the 'phc2sys' tool that will be run in background and periodically
                          synchronize the host and NIC clock.
          * proc - the 'Proc' or 'SSH' object that defines the host to configure the ETF qdisc on
                   (default is the local host). This object will keep a 'proc' reference and use it
                   in various methods.

        Note about phc2sys.

        When ETF qdisc offloads packets to the NIC, it is important to keep host and NIC times in
        sync, because Linux kernel API accept absolute time values to send the packets at, and these
        values are passed down to the NIC as-is. If NIC's time is different, the packets will be
        sent at incorrect time or just dropped causing errors like "missing deadline".
        """

        self._netif = None
        self._old_tc_err_msg = None
        self._phc2sys_proc = None

        self._ifname = ifname
        self._tc_bin = FSHelpers.which(tc_bin, proc=proc)
        self._handover_delta = int(handover_delta * 1000)
        self._phc2sys_bin = FSHelpers.which(phc2sys_bin, proc=proc)
        self._proc = proc

        self._netif = _NetIface.NetIface(ifname, proc=proc)

        self._old_tc_err_msg = f"the 'tc' tool installed{self._proc.hostmsg} is not new enough " \
                               f"and does not support the ETF qdisc.\nPlease, install 'tc' " \
                               f"version '181023' or greater.\nThe 'tc' tool is part of the " \
                               f"'iproute2' project. Run 'tc -V' to check its version."
Beispiel #14
0
def _get_ifinfos(proc):
    """
    For every network interfaces backed by a real device on the system defined by 'proc', yield the
    following tuples:
    * interface name
    * device HW address
    """

    for ifname, path, typ in FSHelpers.lsdir(_SYSFSBASE, proc=proc):
        if typ != "@":
            # We expect a symlink.
            continue

        devpath = None
        with contextlib.suppress(Error):
            devpath = FSHelpers.abspath(path / "device", proc=proc)
        if not devpath:
            continue

        yield ifname, devpath.name
Beispiel #15
0
    def _check_ip_tool_present(self):
        """Verifies that the "ip" tool is available."""

        if self._ip_tool_present:
            return

        if not FSHelpers.which("ip", default=None, proc=self._proc):
            raise ErrorNotSupported(
                f"the 'ip' tool is not installed{self._proc.hostmsg}.\nThis "
                f"tool is part of the 'iproute2' project, please install it.")
        self._ip_tool_present = True
Beispiel #16
0
    def __init__(self, proc=None):
        """Class constructor."""

        if not proc:
            proc = Procs.Proc()

        self._proc = proc
        self._lspci_bin = "lspci"

        if not FSHelpers.which(self._lspci_bin, default=None, proc=proc):
            raise ErrorNotSupported(
                f"the '{self._lspci_bin}' tool is not installed{proc.hostmsg}")
Beispiel #17
0
    def get_newest_mtime(path):
        """Scan items in 'path' and return newest modification time among entries found."""

        newest = 0
        for _, fpath, _ in FSHelpers.lsdir(path, must_exist=False):
            mtime = os.path.getmtime(fpath)
            if mtime > newest:
                newest = mtime

        if not newest:
            raise Error(f"no files found in '{path}'")
        return newest
Beispiel #18
0
    def __init__(self,
                 proc,
                 ifname,
                 res,
                 ndlrunner_bin,
                 cstats=None,
                 ts_bin=None,
                 ldist=None):
        """
        The class constructor. The arguments are as follows.
          * proc - the 'Proc' or 'SSH' object that defines the host to run the measurements on.
          * ifname - the network interface name to use for measuring the latency.
          * res - the 'WORawResult' object to store the results at.
          * ndlrunner_bin - path to the 'ndlrunner' helper.
          * cstats - True: collect C-state statistics, fail if 'turbostat' not found.
                     None: collect C-state statistics, only if 'turbostat' is found.
                     False: do not collect C-state statistics.
          * ts_bin - path to the 'turbostat' tool, default is just "turbostat".
          * ldist - for how far in the future the delayed network packets should be scheduled in
                    microseconds. Default is [5000, 10000] microseconds.
        """

        self._proc = proc
        self._ifname = ifname
        self._res = res
        self._ndlrunner_bin = ndlrunner_bin
        self._run_ts = cstats
        self._ts_bin = ts_bin
        self._ldist = ldist
        self._ts_heading = ""

        self._ndl_lines = None
        self._drv = None
        self._rtd_path = None
        self._ndlrunner = None
        self._colmap = None
        self._progress = None
        self._max_rtd = 0
        self._post_trigger = None
        self._post_trigger_thresh = None
        self._etfqdisc = None
        self._nmcli = None
        self._netif = None

        self._verify_input_args()
        self._cstate_stats_init()

        self._progress = _ProgressLine.ProgressLine(period=1)
        self._drv = KernelModule.KernelModule(proc, "ndl")

        mntpath = FSHelpers.mount_debugfs(proc=proc)
        self._rtd_path = mntpath.joinpath(f"{self._drv.name}/rtd")
        self._etfqdisc = _ETFQdisc.ETFQdisc(ifname, proc=proc)
Beispiel #19
0
def _get_module_path(proc, name):
    """Return path to installed module. Return 'None', if module not found."""

    cmd = f"modinfo -n {name}"
    stdout, _, exitcode = proc.run(cmd)
    if exitcode != 0:
        return None

    modpath = Path(stdout.strip())
    if FSHelpers.isfile(modpath, proc):
        return modpath
    return None
Beispiel #20
0
def _deploy(args):
    """Deploy helpers and drivers."""

    with contextlib.closing(get_proc(args, args.ihost)) as iproc, \
         contextlib.closing(get_proc(args, args.bhost)) as bproc:
        remotesrc = args.bhost != "localhost"
        remotedst = args.ihost != "localhost"

        if hasattr(args, "helpersrc"):
            helpersdst = args.tmpdir / "helpers_deployed"
            _LOG.debug("Deploying helpers to '%s'%s", helpersdst, bproc.hostmsg)
            cmd = f"make -C '{args.helpersrc}' install PREFIX='{helpersdst}'"
            stdout, stderr = bproc.run_verify(cmd)
            _log_cmd_output(args, stdout, stderr)

            iproc.rsync(str(helpersdst) + "/", args.helpers_path,
                        remotesrc=remotesrc, remotedst=remotedst)

        if hasattr(args, "drvsrc"):
            dstdir = args.kmodpath.joinpath(_DRV_SRC_SUBPATH)
            FSHelpers.mkdir(dstdir, parents=True, exist_ok=True, proc=iproc)

            for name in _get_deployables(args.drvsrc, bproc):
                installed_module = _get_module_path(iproc, name)
                srcpath = args.drvsrc.joinpath(f"{name}.ko")
                dstpath = dstdir.joinpath(f"{name}.ko")
                _LOG.info("Deploying driver '%s' to '%s'%s", name, dstpath, iproc.hostmsg)
                iproc.rsync(srcpath, dstpath, remotesrc=remotesrc, remotedst=remotedst)

                if installed_module and installed_module.resolve() != dstpath.resolve():
                    _LOG.debug("removing old module '%s'%s", installed_module, iproc.hostmsg)
                    iproc.run_verify(f"rm -f '{installed_module}'")

            stdout, stderr = iproc.run_verify(f"depmod -a -- '{args.kver}'")
            _log_cmd_output(args, stdout, stderr)

            # Potentially the deployed driver may crash the system before it gets to write-back data
            # to the file-system (e.g., what 'depmod' modified). This may lead to subsequent boot
            # problems. So sync the file-system now.
            iproc.run_verify("sync")
Beispiel #21
0
    def __init__(self, name):
        """
        The class constructor. The arguments are as follows.
          * name - name of the tool to load the definitions for (e.g., 'wult').
        """

        self.name = name
        self.info = None
        self.vanilla_info = None

        path = FSHelpers.search_for_app_data("wult", Path(f"defs/{name}.yml"),
                                             pathdescr=f"{name} datapoints definitions file")
        self.info = self.vanilla_info = YAML.load(path)
Beispiel #22
0
    def _verify_input_args(self):
        """Verify and adjust the constructor input arguments."""

        if not self._ldist:
            self._ldist = "5000, 10000"

        self._ldist = _Common.validate_ldist(self._ldist)

        # Validate the 'ndlrunner' helper path.
        if not FSHelpers.isexe(self._ndlrunner_bin, proc=self._proc):
            raise Error(
                f"bad 'ndlrunner' helper path '{self._ndlrunner_bin}' - does not exist"
                f"{self._proc.hostmsg} or not an executable file")
Beispiel #23
0
    def _get_cstate_indexes(self, cpu):
        """Yield tuples of of C-state indexes and sysfs paths for cpu number 'cpu'."""

        basedir = self._sysfs_base / f"cpu{cpu}" / "cpuidle"
        name = None
        for name, path, typ in FSHelpers.lsdir(basedir, proc=self._proc):
            errmsg = f"unexpected entry '{name}' in '{basedir}'{self._proc.hostmsg}"
            if typ != "/" or not name.startswith("state"):
                raise Error(errmsg)
            index = name[len("state"):]
            if not Trivial.is_int(index):
                raise Error(errmsg)
            yield int(index), Path(path)

        if name is None:
            raise Error(f"C-states are not supported{self._proc.hostmsg}")
Beispiel #24
0
    def __init__(self, proc=None):
        """
        Initialize a class instance for the host associated with the 'proc' object. By default it is
        is going to be the local host, but 'proc' can be used to pass a connected 'SSH' object, in
        which case all operation will be done on the remote host. This object will keep a 'proc'
        reference and use it in various methods.
        """

        if not proc:
            proc = Procs.Proc()

        self._proc = proc
        self._saved_managed = OrderedDict()

        if not FSHelpers.which("nmcli", default=None, proc=proc):
            raise ErrorNotSupported(
                f"the 'nmcli' tool is not installed{proc.hostmsg}")
Beispiel #25
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
Beispiel #26
0
    def _cstate_stats_init(self):
        """Initialize information related to C-state statistics and the 'turbostat' tool."""

        if self._run_ts is False:
            if not self._ts_bin:
                return
            raise Error(
                "path to 'turbostat' tool given, but C-state statistics collection is "
                "disabled")

        if not self._ts_bin:
            self._ts_bin = FSHelpers.which("turbostat",
                                           default=None,
                                           proc=self._proc)

        if not self._ts_bin:
            msg = f"cannot collect C-state statistics: the 'turbostat' tool was not found" \
                  f"{self._proc.hostmsg}"
            if self._run_ts is True:
                raise ErrorNotSupported(msg)

            self._run_ts = False
            _LOG.info(msg)
            return

        # Make sure turbostat version is '19.08.31' or newer. Turbostat versions are dates (e.g.,
        # version '19.03.20' is Mar 20 2019), so we can use the 'datetime' module to compare the
        # versions.
        version = self._get_turbostat_version()
        vdate = (int(val) for val in version.split("."))
        if datetime.datetime(*vdate) < datetime.datetime(19, 8, 31):
            msg = f"cannot collect C-state statistics: too old turbostat version '{version}', " \
                  f"use turbostat version '19.08.31' or newer\n(checked {self._ts_bin}" \
                  f"{self._proc.hostmsg}"
            if self._run_ts:
                raise ErrorNotSupported(msg)

            self._run_ts = False
            _LOG.info(msg)
        else:
            self._run_ts = True
            self._build_colmap()
Beispiel #27
0
    def __init__(self, devid, cpunum, proc, ldist=None, force=False):
        """
        Initialize a class instance for a PCI device 'devid'. The arguments are as follows.
          * devid - the device "ID", which can be a PCI address or a network interface name.
          * cpunum - the measured CPU number.
          * proc - the host to operate on. This object will keep a 'proc' reference and use it in
                   various methods
          * ldist - how far in future should the delayed event be scheduled.
          * force - initialize measurement device, even if it is already in use.
        """

        self._cpunum = cpunum
        self._proc = proc
        self._ldist = ldist
        self._drv = None
        self._saved_drvname = None
        self.dev = None
        self._basedir = None
        self._enable_path = None
        self._main_drv = None

        # This is a debugging option that allows to disable automatic wult modules unloading on
        # 'close()'.
        self.unload = True
        # Whether kernel messages should be monitored. They are very useful if something goes wrong.
        self.dmesg = True

        self.dev = Devices.WultDevice(devid, cpunum, proc, force=force)

        self._main_drv = KernelModule.KernelModule(proc, "wult")
        self._drv = KernelModule.KernelModule(proc, self.dev.drvname)

        mntpoint = FSHelpers.mount_debugfs(proc=proc)
        self._basedir = mntpoint / "wult"
        self._enable_path = self._basedir / "enabled"

        msg = f"Compatible device '{self.dev.info['name']}'{proc.hostmsg}:\n" \
              f" * Device ID: {self.dev.info['devid']}\n" \
              f"   - {self.dev.info['descr']}"
        _LOG.info(msg)
Beispiel #28
0
    def set_post_trigger(self, path, thresh=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 latency value in
            nanoseconds.
          * thresh - the post-trigger threshold. By default, the trigger program is executed on evey
            datapoint. But if a threshold is provided, the trigger program will be executed only
            when latency exceeds the threshold.
        """

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

        self._post_trigger = path
        if thresh is not None:
            if not Trivial.is_int(thresh):
                raise Error(
                    f"bad post-trigger threshold value '{thresh}', should be an integer "
                    f"amount of nanoseconds")
            self._post_trigger_thresh = int(thresh)
Beispiel #29
0
    def __init__(self, ifid, proc=None):
        """
        Initialize a class instance network interface corresponding to 'ifid' on the host associated
        with the 'proc' object. The 'ifid' argumen can be either the network interface name or its
        hardware address (e.g., the PCI address of the network card corresponding to the network
        interface).

        By default this class is intialized for the local host, but 'proc' can be used to pass a
        connected 'SSH' object, in which case all operation will be done on the remote host. This
        object will keep a 'proc' reference and use it in various methods.
        """

        if not proc:
            proc = Procs.Proc()

        self._ifid = ifid
        self._proc = proc
        self.ifname = None
        self.hwaddr = None
        self._sysfsbase = None
        self._saved_ip_info = {}
        self._ip_tool_present = None

        sysfsbase = _SYSFSBASE.joinpath(ifid)
        if FSHelpers.isdir(sysfsbase, proc=proc):
            # 'ifid' is a network interface name.
            self.ifname = ifid
            self._sysfsbase = sysfsbase
            self.hwaddr = self._get_hw_addr()
        else:
            # 'ifid' is probably a HW address (e.g., PCI address).
            self.ifname = self._hw_addr_to_ifname()
            if not self.ifname:
                self._raise_iface_not_found()
            self.hwaddr = ifid
            self._sysfsbase = _SYSFSBASE.joinpath(self.ifname)
Beispiel #30
0
def _deploy_prepare(args, toolname, minkver):
    """
    Validate command-line arguments of the "deploy" command and prepare for builing the helpers and
    drivers. The arguments are as follows.
      o args - the command line arguments.
      o toolname - name of the tool being deployed (e.g., 'ndl').
      o minkver - the minimum required version number.
    """

    args.tmpdir = None
    args.kver = None

    if not args.ihost:
        args.ihost = "localhost"
    if not args.bhost:
        args.bhost = args.ihost

    if args.ihost != args.bhost and not args.bhost == "localhost":
        raise Error("build host (--build-host) must be the local host or the same as deploy host "
                    "(--host)")

    if args.ihost == "localhost" and args.bhost == "localhost":
        for attr in ("username", "privkey", "timeout"):
            if getattr(args, attr) is not None:
                _LOG.warning("ignoring the '--%s' option as it not useful for a local host", attr)

    if not args.timeout:
        args.timeout = 8
    else:
        args.timeout = Trivial.str_to_num(args.timeout)
    if not args.username:
        args.username = "******"

    if args.privkey and not args.privkey.is_dir():
        raise Error(f"path '{args.privkey}' does not exist or it is not a directory")

    if hasattr(args, "drvsrc"):
        if not args.drvsrc:
            args.drvsrc = FSHelpers.search_for_app_data("wult", _DRV_SRC_SUBPATH/f"{toolname}",
                                                        pathdescr=f"{toolname} drivers sources")

        if not args.drvsrc.is_dir():
            raise Error(f"path '{args.drvsrc}' does not exist or it is not a directory")

    if hasattr(args, "helpersrc"):
        if not args.helpersrc:
            args.helpersrc = FSHelpers.search_for_app_data("wult",
                                                           _HELPERS_SRC_SUBPATH/f"{toolname}",
                                                           pathdescr=f"{toolname} helper sources")
        if not args.helpersrc.is_dir():
            raise Error(f"path '{args.helpersrc}' does not exist or it is not a directory")

    with contextlib.closing(get_proc(args, args.bhost)) as proc:
        if not FSHelpers.which("make", default=None, proc=proc):
            raise Error(f"please, install the 'make' tool{proc.hostmsg}")

        if not args.ksrc:
            args.kver = KernelVersion.get_kver(proc=proc)
            if not args.ksrc:
                args.ksrc = Path(f"/lib/modules/{args.kver}/build")
        else:
            args.ksrc = FSHelpers.abspath(args.ksrc, proc=proc)

        if not FSHelpers.isdir(args.ksrc, proc=proc):
            raise Error(f"kernel sources directory '{args.ksrc}' does not exist{proc.hostmsg}")

        if not args.kver:
            args.kver = KernelVersion.get_kver_ktree(args.ksrc, proc=proc)

        _LOG.info("Kernel sources path: %s", args.ksrc)
        _LOG.info("Kernel version: %s", args.kver)

        if KernelVersion.kver_lt(args.kver, minkver):
            raise Error(f"version of the kernel{proc.hostmsg} is {args.kver}, and it is not new "
                        f"enough.\nPlease, use kernel version {minkver} or newer.")

        args.tmpdir = FSHelpers.mktemp(prefix=f"{toolname}-", proc=proc)

        if hasattr(args, "drvsrc"):
            _LOG.debug("copying the drivers to %s:\n   '%s' -> '%s'",
                       proc.hostname, args.drvsrc, args.tmpdir)
            proc.rsync(f"{args.drvsrc}/", args.tmpdir / "drivers", remotesrc=False, remotedst=True)
            args.drvsrc = args.tmpdir / "drivers"
            _LOG.info("Drivers will be compiled on host '%s'", proc.hostname)

        if hasattr(args, "helpersrc"):
            _LOG.debug("copying the helpers to %s:\n  '%s' -> '%s'",
                       proc.hostname, args.helpersrc, args.tmpdir)
            proc.rsync(f"{args.helpersrc}/", args.tmpdir / "helpers", remotesrc=False,
                       remotedst=True)
            args.helpersrc = args.tmpdir / "helpers"
            _LOG.info("Helpers will be compiled on host '%s'", proc.hostname)

    with contextlib.closing(get_proc(args, args.ihost)) as proc:
        if not args.kmodpath:
            args.kmodpath = Path(f"/lib/modules/{args.kver}")
        if not FSHelpers.isdir(args.kmodpath, proc=proc):
            raise Error(f"kernel modules directory '{args.kmodpath}' does not exist{proc.hostmsg}")

        _LOG.info("Drivers will be deployed to '%s'%s", args.kmodpath, proc.hostmsg)
        _LOG.info("Kernel modules path%s: %s", proc.hostmsg, args.kmodpath)

        if hasattr(args, "helpersrc"):
            if not args.helpers_path:
                args.helpers_path = get_helpers_deploy_path(proc, toolname)
            _LOG.info("Helpers will be deployed to '%s'%s", args.helpers_path, proc.hostmsg)