Exemple #1
0
    def check_version(self):
        msg = ('The IIO drivers running on {} ({}) are out-of-date; '
               'devlib requires {} or later.')

        if iio.version[:2] < self.MINIMAL_HOST_IIO_DRIVERS_VERSION:
            ver_str = '.'.join(map(str, iio.version[:2]))
            min_str = '.'.join(map(str, self.MINIMAL_HOST_IIO_DRIVERS_VERSION))
            raise HostError(msg.format('this host', ver_str, min_str))

        if self.version[:2] < self.MINIMAL_ACME_IIO_DRIVERS_VERSION:
            ver_str = '.'.join(map(str, self.version[:2]))
            min_str = '.'.join(map(str, self.MINIMAL_ACME_IIO_DRIVERS_VERSION))
            raise TargetError(msg.format('the BBB', ver_str, min_str))
Exemple #2
0
    def start_capturing(self):
        if not self.wanted_channels:
            raise TargetError('No active channel: aborting.')

        self.active_channels = self.wanted_channels.copy()
        if self.high_resolution:
            self.active_channels &= self.uncomputable_channels
            for channel, dependencies in self.computable_channels.items():
                if channel in self.wanted_channels:
                    self.active_channels |= dependencies

        self.work_done.clear()
        self.collector = self.sample_collector()
        self.collector.daemon = True
        self.collector.start()
Exemple #3
0
    def check_version(self):
        super(BaylibreAcmeNetworkInstrument, self).check_version()

        cmd = r"""sed -nr 's/^VERSION_ID="(.+)"$/\1/p' < /etc/os-release"""
        try:
            ver_str = self._ssh(cmd).rstrip()
            ver = tuple(map(int, ver_str.split('.')))
        except Exception as e:
            self.logger.debug(
                'Unable to verify ACME SD image version through SSH: {}'.
                format(e))
        else:
            if ver < self.MINIMAL_ACME_SD_IMAGE_VERSION:
                min_str = '.'.join(map(str,
                                       self.MINIMAL_ACME_SD_IMAGE_VERSION))
                msg = (
                    'The ACME SD image for the BBB (ver. {}) is out-of-date; '
                    'devlib requires {} or later.')
                raise TargetError(msg.format(ver_str, min_str))
Exemple #4
0
    def __init__(self, iio_device):

        if iio_device.name != self.IIO_DEVICE_NAME:
            msg = 'IIO device is {}; expected {}'
            raise TargetError(msg.format(iio_device.name,
                                         self.IIO_DEVICE_NAME))

        self.iio_device = iio_device
        self.absolute_timestamps = False
        self.high_resolution = True
        self.buffer_samples_count = None
        self.buffer_is_circular = False

        self.collector = None
        self.work_done = threading.Event()
        self.collector_exception = None

        self.data = collections.OrderedDict()

        channels = {
            'timestamp': 'timestamp',
            'shunt': 'voltage0',
            'voltage': 'voltage1',  # bus
            'power': 'power2',
            'current': 'current3',
        }
        self.computable_channels = {
            'current': {'shunt'},
            'power': {'shunt', 'voltage'}
        }
        self.uncomputable_channels = set(channels) - set(
            self.computable_channels)
        self.channels = {
            k: IIOINA226Channel(self.iio_device.find_channel(v))
            for k, v in channels.items()
        }
        # We distinguish between "output" channels (as seen by the user of this
        # class) and "hardware" channels (as requested from the INA226).
        # This is necessary because of the 'high_resolution' feature which
        # requires outputting computed channels:
        self.active_channels = set()  # "hardware" channels
        self.wanted_channels = set()  # "output" channels
Exemple #5
0
 def _ssh(self, cmd=''):
     """Connections are assumed to be rare."""
     if self.ssh_connection is None:
         raise TargetError('No SSH connection; see log.')
     return self.ssh_connection.execute(cmd)
Exemple #6
0
 def read_sge_file(path):
     try:
         return sge_file_values[path]
     except KeyError as e:
         raise TargetError('No such file: {}'.format(e))
Exemple #7
0
    def from_sd_target(cls, target, filename=
            '/proc/sys/kernel/sched_domain/cpu{}/domain{}/group{}/energy/{}'):
        """
        Create an EnergyModel by reading a target filesystem

        This uses the sysctl added by EAS pathces to exposes the cap_states and
        idle_states fields for each sched_group. This feature depends on
        CONFIG_SCHED_DEBUG, and is not upstream in mainline Linux (as of v4.11),
        so this method is only tested with Android kernels.

        The kernel doesn't have an power domain data, so this method assumes
        that all CPUs are totally independent wrt. idle states - the EnergyModel
        constructed won't be aware of the topological dependencies for entering
        "cluster" idle states.

        Assumes the energy model has two-levels (plus the root) - a level for
        CPUs and a level for 'clusters'.

        :param target: Devlib target object to read filesystem from. Must have
                       cpufreq and cpuidle modules enabled.
        :returns: Constructed EnergyModel object based on the parameters
                  reported by the target.
        """
        if 'cpufreq' not in target.modules:
            raise TargetError('Requires cpufreq devlib module. Please ensure '
                               '"cpufreq" is listed in your target/test modules')
        if 'cpuidle' not in target.modules:
            raise TargetError('Requires cpuidle devlib module. Please ensure '
                               '"cpuidle" is listed in your target/test modules')

        def sge_path(cpu, domain, group, field):
            return filename.format(cpu, domain, group, field)

        # Read all the files we might need in one go, otherwise this will take
        # ages.
        sge_globs = [sge_path('**', '**', '**', 'cap_states'),
                     sge_path('**', '**', '**', 'idle_states')]
        sge_file_values = read_multiple_oneline_files(target, sge_globs)

        if not sge_file_values:
            raise TargetError('Energy Model not exposed in sysfs. '
                              'Check CONFIG_SCHED_DEBUG is enabled.')

        # These functions read the cap_states and idle_states vectors for the
        # first sched_group in the sched_domain for a given CPU at a given
        # level. That first group will include the given CPU. So
        # read_active_states(0, 0) will give the CPU-level active_states for
        # CPU0 and read_active_states(0, 1) will give the "cluster"-level
        # active_states for the "cluster" that contains CPU0.

        def read_sge_file(path):
            try:
                return sge_file_values[path]
            except KeyError as e:
                raise TargetError('No such file: {}'.format(e))

        def read_active_states(cpu, domain_level):
            cap_states_path = sge_path(cpu, domain_level, 0, 'cap_states')
            cap_states_strs = read_sge_file(cap_states_path).split()

            # cap_states lists the capacity of each state followed by its power,
            # in increasing order. The `zip` call does this:
            #   [c0, p0, c1, p1, c2, p2] -> [(c0, p0), (c1, p1), (c2, p2)]
            cap_states = [ActiveState(capacity=int(c), power=int(p))
                          for c, p in zip(cap_states_strs[0::2],
                                          cap_states_strs[1::2])]
            freqs = target.cpufreq.list_frequencies(cpu)
            return OrderedDict(zip(sorted(freqs), cap_states))

        def read_idle_states(cpu, domain_level):
            idle_states_path = sge_path(cpu, domain_level, 0, 'idle_states')
            idle_states_strs = read_sge_file(idle_states_path).split()

            # get_states should return the state names in increasing depth order
            names = [s.name for s in target.cpuidle.get_states(cpu)]
            # idle_states is a list of power values in increasing order of
            # idle-depth/decreasing order of power.
            return OrderedDict(zip(names, [int(p) for p in idle_states_strs]))

        # Read the CPU-level data from sched_domain level 0
        cpus = range(target.number_of_cpus)
        cpu_nodes = []
        for cpu in cpus:
            node = EnergyModelNode(
                cpu=cpu,
                active_states=read_active_states(cpu, 0),
                idle_states=read_idle_states(cpu, 0))
            cpu_nodes.append(node)

        # Read the "cluster" level data from sched_domain level 1
        core_group_nodes = []
        for core_group in cls._find_core_groups(target):
            node=EnergyModelNode(
                children=[cpu_nodes[c] for c in core_group],
                active_states=read_active_states(core_group[0], 1),
                idle_states=read_idle_states(core_group[0], 1))
            core_group_nodes.append(node)

        root = EnergyModelRoot(children=core_group_nodes)

        # Use cpufreq to figure out the frequency domains
        freq_domains = []
        remaining_cpus = set(cpus)
        while remaining_cpus:
            cpu = next(iter(remaining_cpus))
            dom = target.cpufreq.get_related_cpus(cpu)
            freq_domains.append(dom)
            remaining_cpus = remaining_cpus.difference(dom)

        # We don't have a way to read the power domains from sysfs (the kernel
        # isn't even aware of them) so we'll just have to assume each CPU is its
        # own power domain and all idle states are independent of each other.
        cpu_pds = []
        for cpu in cpus:
            names = [s.name for s in target.cpuidle.get_states(cpu)]
            cpu_pds.append(PowerDomain(cpu=cpu, idle_states=names))

        root_pd=PowerDomain(children=cpu_pds, idle_states=[])

        return cls(root_node=root,
                   root_power_domain=root_pd,
                   freq_domains=freq_domains)
Exemple #8
0
    def from_simplifiedEM_target(cls, target,
            directory='/sys/devices/system/cpu/energy_model'):
        """
        Create an EnergyModel by reading a target filesystem on a device with
        the new Simplified Energy Model present.

        This uses the energy_model sysctl added by EAS patches to exposes
        the frequency domains, together with a tuple of capacity, frequency
        and active power for each CPU. This feature is not upstream in mainline
        Linux (as of v4.17), and only exists in Android kernels later than
        android-4.14.

        Wrt. idle states - the EnergyModel constructed won't be aware of
        any power data or topological dependencies for entering "cluster"
        idle states since the simplified model has no such concept.

        Initialises only Active States for CPUs and clears all other levels.

        :param target: Devlib target object to read filesystem from. Must have
                       cpufreq and cpuidle modules enabled.
        :returns: Constructed EnergyModel object based on the parameters
                  reported by the target.
        """
        if 'cpuidle' not in target.modules:
            raise TargetError('Requires cpuidle devlib module. Please ensure '
                               '"cpuidle" is listed in your target/test modules')

        # Simplified EM on-disk format (for each frequency domain):
        #    /sys/devices/system/cpu/energy_model/<frequency_domain>/..
        #        ../capacity
        #           contains a space-separated list of capacities in increasing order
        #        ../cpus
        #           cpulist-formatted representation of the cpus in the frequency domain
        #        ../frequency
        #           space-separated list of frequencies in corresponding order to capacities
        #        ../power
        #           space-separated list of power consumption in corresponding order to capacities
        # taken together, the contents of capacity, frequency and power give you the required
        # tuple for ActiveStates.
        # hence, domain should be supplied as a glob, and fields should be
        #   capacity, cpus, frequency, power

        sysfs_em = target.read_tree_values(directory, depth=3)

        if not sysfs_em:
            raise TargetError('Simplified Energy Model not exposed '
                              'at {} in sysfs.'.format(directory))

        cpu_to_fdom = {}
        for fd, fields in sysfs_em.iteritems():
            cpus = ranges_to_list(fields["cpus"])
            for cpu in cpus:
                cpu_to_fdom[cpu] = fd
            sysfs_em[fd]['cpus'] = cpus
            sysfs_em[fd]['frequency'] = map(int, sysfs_em[fd]['frequency'].split(' '))
            sysfs_em[fd]['power'] =  map(int, sysfs_em[fd]['power'].split(' '))

            # Compute the capacity of the CPUs at each OPP with a linerar
            # mapping to the frequencies
            sysfs = '/sys/devices/system/cpu/cpu{}/cpu_capacity'
            cap = target.read_value(sysfs.format(cpus[0]), int)
            max_freq = max(sysfs_em[fd]['frequency'])
            caps = [f * cap / max_freq for f in sysfs_em[fd]['frequency']]
            sysfs_em[fd]['capacity'] = caps

        def read_active_states(cpu):
            fd = sysfs_em[cpu_to_fdom[cpu]]
            cstates = zip(fd['capacity'], fd['power'])
            active_states = [ActiveState(c, p) for c, p in cstates]
            return OrderedDict(zip(fd['frequency'], active_states))

        def read_idle_states(cpu):
            # idle states are not supported in the new model
            # record 0 power for them all, but name them according to target
            names = [s.name for s in target.cpuidle.get_states(cpu)]
            return OrderedDict((name, 0) for name in names)

        # Read the CPU-level data
        cpus = range(target.number_of_cpus)
        cpu_nodes = []
        for cpu in cpus:
            node = EnergyModelNode(
                cpu=cpu,
                active_states=read_active_states(cpu),
                idle_states=read_idle_states(cpu))
            cpu_nodes.append(node)

        root = EnergyModelRoot(children=cpu_nodes)
        freq_domains = [sysfs_em[fdom]['cpus'] for fdom in sysfs_em]

        # We don't have a way to read the idle power domains from sysfs (the kernel
        # isn't even aware of them) so we'll just have to assume each CPU is its
        # own power domain and all idle states are independent of each other.
        cpu_pds = []
        for cpu in cpus:
            names = [s.name for s in target.cpuidle.get_states(cpu)]
            cpu_pds.append(PowerDomain(cpu=cpu, idle_states=names))

        root_pd = PowerDomain(children=cpu_pds, idle_states=[])
        return cls(root_node=root,
                   root_power_domain=root_pd,
                   freq_domains=freq_domains)
Exemple #9
0
    def from_target(cls, target):
        """
        Create an EnergyModel by reading a target filesystem

        If present, load an EM provided via dt using from_sd_target, since these
        devices make the full EM available via the sched domain in sysfs. If
        there is no EM at this location, attempt to load the simplified EM
        made available via dedicated sysfs files.

        :param target: Devlib target object to read filesystem from. Must have
                       cpufreq and cpuidle modules enabled.
        :returns: Constructed EnergyModel object based on the parameters
                  reported by the target.
        """
        _log = logging.getLogger('EMReader')

        # To add a new EM reader type, the following is required:
        # 1. Create an inline function to test for EM presence which takes a
        #    target as the first parameter. Any exceptions raised here will
        #    be caught in the loader loop.
        # 2. Create a function which returns an EnergyModel as a member of this
        #    class, also with a target as the first parameter.
        # 3. Add an entry to the em_loaders dict where 'check' contains the
        #    inline function and 'load' contains the class member function
        # 4. If you need any additional data, add it to the em_loaders dict - any
        #    additional keys will be passed to both 'check' and 'load' functions
        #    as named parameters.

        # Utility functions to determine if we should try to use a particular
        # EM loader function.
        def em_present_in_sd(target, filename=None):
            cpu = target.list_online_cpus()[0]
            f = filename.format(cpu, 0, 0, 'cap_states')
            return target.file_exists(f)
        def simplified_em_present_in_cpusysfs(target, directory=None):
            return target.directory_exists(directory)

        # em_loaders dictionary joins EM loaders and the identifying functions
        # with any associated metadata
        em_loaders = {
            'sd'    : { 'check': em_present_in_sd,
                        'load': cls.from_sd_target,
                        'filename': '/proc/sys/kernel/sched_domain/cpu{}/domain{}/group{}/energy/{}' },
            'sysfs' : { 'check': simplified_em_present_in_cpusysfs,
                        'load': cls.from_simplifiedEM_target,
                        'directory': '/sys/devices/system/cpu/energy_model' }
                     }

        for loader_type in em_loaders:
            args = dict(em_loaders[loader_type])
            check = args.pop('check')
            load = args.pop('load')
            try:
                em_present = check(target, **args)
            except Exception:
                em_present = False
            if em_present:
                _log.info('Attempting to load EM using {}'.format(load.__name__))
                return load(target, **args)

        raise TargetError('Unable to probe for energy model on target.')