def _run_rtapp(cls, target, res_dir, profile, ftrace_coll=None, cg_cfg=None): wload = RTA.by_profile(target, "rta_{}".format(cls.__name__.lower()), profile, res_dir=res_dir) trace_path = os.path.join(res_dir, cls.TRACE_PATH) dmesg_path = os.path.join(res_dir, cls.DMESG_PATH) ftrace_coll = ftrace_coll or FtraceCollector.from_conf( target, cls.ftrace_conf) dmesg_coll = DmesgCollector(target) cgroup = cls._target_configure_cgroup(target, cg_cfg) as_root = cgroup is not None with dmesg_coll, ftrace_coll, target.freeze_userspace(): wload.run(cgroup=cgroup, as_root=as_root) ftrace_coll.get_trace(trace_path) dmesg_coll.get_trace(dmesg_path) return trace_path
def _do_test(self, profile, exp_phases): rtapp = RTA.by_profile(self.target, name='test', profile=profile, res_dir=self.res_dir, calibration=None) with open(rtapp.local_json) as f: conf = json.load(f, object_pairs_hook=OrderedDict) # Check that the configuration looks like we expect it to phases = list(conf['tasks']['test_task']['phases'].values()) self.assertEqual(len(phases), len(exp_phases), 'Wrong number of phases') for phase, exp_phase in zip(phases, exp_phases): self.assertDictEqual(phase, exp_phase) # Try running the workload and check that it produces the expected log # files rtapp.run() # rtapp_cmds = [c for c in self.target.executed_commands if 'rt-app' in c] # self.assertListEqual(rtapp_cmds, [self.get_expected_command(rtapp)]) self.assert_output_file_exists('output.log') self.assert_output_file_exists('test.json') self.assert_output_file_exists('rt-app-test_task-0.log') self.assert_can_read_logfile(exp_tasks=['test_task'])
def run_rtapp(cls, target, res_dir, profile=None, ftrace_coll=None, cg_cfg=None): """ Run the given RTA profile on the target, and collect an ftrace trace. :param target: target to execute the workload on. :type target: lisa.target.Target :param res_dir: Artifact folder where the artifacts will be stored. :type res_dir: str or lisa.utils.ArtifactPath :param profile: ``rt-app`` profile, as a dictionary of ``dict(task_name, RTATask)``. If ``None``, :meth:`~lisa.tests.base.RTATestBundle.get_rtapp_profile` is called with ``target.plat_info``. :type profile: dict(str, lisa.wlgen.rta.RTATask) :param ftrace_coll: Ftrace collector to use to record the trace. This allows recording extra events compared to the default one, which is based on the ``ftrace_conf`` class attribute. :type ftrace_coll: lisa.trace.FtraceCollector :param cg_cfg: CGroup configuration dictionary. If ``None``, :meth:`lisa.tests.base.RTATestBundle.get_cgroup_configuration` is called with ``target.plat_info``. :type cg_cfg: dict """ trace_path = ArtifactPath.join(res_dir, cls.TRACE_PATH) dmesg_path = ArtifactPath.join(res_dir, cls.DMESG_PATH) ftrace_coll = ftrace_coll or FtraceCollector.from_conf( target, cls.ftrace_conf) dmesg_coll = DmesgCollector(target) profile = profile or cls.get_rtapp_profile(target.plat_info) cg_cfg = cg_cfg or cls.get_cgroup_configuration(target.plat_info) wload = RTA.by_profile(target, "rta_{}".format(cls.__name__.lower()), profile, res_dir=res_dir) cgroup = cls._target_configure_cgroup(target, cg_cfg) as_root = cgroup is not None # Pre-hit the calibration information, in case this is a lazy value. # This avoids polluting the trace and the dmesg output with the # calibration tasks. Since we know that rt-app will always need it for # anything useful, it's reasonable to do it here. target.plat_info['rtapp']['calib'] with dmesg_coll, ftrace_coll, target.freeze_userspace(): wload.run(cgroup=cgroup, as_root=as_root) ftrace_coll.get_trace(trace_path) dmesg_coll.get_trace(dmesg_path) return trace_path
def _do_test(self, profile, exp_phases): rtapp = RTA.from_profile( self.target, name='test', profile=profile, res_dir=self.res_dir, calibration=None, log_stats=True) with rtapp: with open(rtapp.local_json) as f: conf = json.load(f) # Check that the configuration looks like we expect it to phases = list(conf['tasks']['test']['phases'].values()) assert len(phases) == len(exp_phases), 'Wrong number of phases' for phase, exp_phase in zip(phases, exp_phases): assert phase == exp_phase # Try running the workload and check that it produces the expected log # files rtapp.run() # rtapp_cmds = [c for c in self.target.executed_commands if 'rt-app' in c] # assert rtapp_cmds == [self.get_expected_command(rtapp)] self.assert_output_file_exists('stdout.log') self.assert_output_file_exists('stderr.log') self.assert_output_file_exists('test.json') self.assert_output_file_exists('rt-app-test-0.log')
def _do_test(self, profile, exp_phases): rtapp = RTA.by_profile( self.target, name='test', profile=profile, res_dir=self.res_dir, calibration=None, log_stats=True) with open(rtapp.local_json) as f: conf = json.load(f, object_pairs_hook=OrderedDict) # Check that the configuration looks like we expect it to phases = list(conf['tasks']['test']['phases'].values()) assert len(phases) == len(exp_phases), 'Wrong number of phases' for phase, exp_phase in zip(phases, exp_phases): if 'cpus' not in exp_phase: exp_phase = copy.copy(exp_phase) exp_phase.update( cpus=sorted(range(self.target.plat_info['cpus-count'])), nodes_membind=sorted(range(self.target.plat_info['numa-nodes-count'])), ) assert phase == exp_phase # Try running the workload and check that it produces the expected log # files rtapp.run() # rtapp_cmds = [c for c in self.target.executed_commands if 'rt-app' in c] # assert rtapp_cmds == [self.get_expected_command(rtapp)] self.assert_output_file_exists('output.log') self.assert_output_file_exists('test.json') self.assert_output_file_exists('rt-app-test-0.log') self.assert_can_read_logfile(exp_tasks=['test-0'])
def _get_calib_conf(self, calibration): profile = {"test": Periodic()} rtapp = RTA.by_profile( self.target, name='test', res_dir=self.res_dir, profile=profile, calibration=calibration) with open(rtapp.local_json) as fh: return json.load(fh)['global']['calibration']
def _test_custom_smoke(self, calibration): """ Test RTA custom workload Creates an rt-app workload using 'custom' and checks that the json roughly matches the file we provided. If we have root, attempts to run the workload. """ json_path = os.path.join(ASSET_DIR, 'mp3-short.json') with open(json_path, 'r') as fh: str_conf = fh.read() rtapp = RTA.from_str( self.target, name='test', str_conf=str_conf, res_dir=self.res_dir, max_duration_s=5, calibration=calibration, as_root=True, ) with rtapp: with open(rtapp.local_json, 'r') as fh: conf = json.load(fh) tasks = { 'AudioTick', 'AudioOut', 'AudioTrack', 'mp3.decoder', 'OMXCall' } assert conf['tasks'].keys() == tasks # Would like to try running the workload but mp3-short.json has nonzero # 'priority' fields, and we probably don't have permission for that # unless we're root. if self.target.is_rooted: rtapp.run() # rtapp_cmds = [c for c in self.target.executed_commands # if 'rt-app' in c] # assert rtapp_cmds == [self.get_expected_command(rtapp)] self.assert_output_file_exists('stdout.log') self.assert_output_file_exists('stderr.log') self.assert_output_file_exists('test.json')
def compute_rtapp_capacities(conf): """ Compute the capacities that will be used for rtapp. If the CPU capacities are not writeable on the target, the orig capacities will be used, otherwise the capacities adjusted with rtapp calibration will be used. """ writeable = conf['writeable'] orig_capacities = conf['orig'] rtapp_calib = conf['..']['rtapp']['calib'] rtapp_capacities = RTA.get_cpu_capacities_from_calibrations(orig_capacities, rtapp_calib) return rtapp_capacities if writeable else orig_capacities
def _get_calib_conf(self, calibration): profile = { "test": RTAPhase(prop_wload=PeriodicWload( duty_cycle_pct=50, period=100e-3, duration=1, )) } rtapp = RTA.from_profile(self.target, name='test', res_dir=self.res_dir, profile=profile, calibration=calibration) with open(rtapp.local_json) as fh: return json.load(fh)['global']['calibration']
def correct_expected_pelt(cls, plat_info, cpu, signal_value): """ Correct an expected PELT signal from ``rt-app`` based on the calibration values. Since the instruction mix of ``rt-app`` might not be the same as the benchmark that was used to establish CPU capacities, the duty cycle of ``rt-app`` will only be accurate on big CPUs. When we know on which CPU the task actually executed, we can correct the expected value based on the ratio of calibration values and CPU capacities. """ calib = plat_info['rtapp']['calib'] cpu_capacities = plat_info['cpu-capacities'] # Correct the signal mean to what it should have been if rt-app # workload was exactly the same as the one used to establish CPU # capacities true_capacities = RTA.get_cpu_capacities_from_calibrations(calib) return signal_value * cpu_capacities[cpu] / true_capacities[cpu]
def df_rtapp_loop(self, task=None, wlgen_profile=None): """ Dataframe of events generated by each rt-app generated task. :param task: the (optional) rt-app task to filter for :type task: int or str or lisa.trace.TaskID :param wlgen_profile: Dictionary of rt-app task names to :class:`~lisa.wlgen.rta.RTAPhase` used to resolve phase name from the trace and will add a ``properties`` column with the :class:`~lisa.wlgen.rta.RTAPhase` properties. :type wlgen_profile: dict(str, lisa.wlgen.rta.RTAPhase) or None :returns: a :class:`pandas.DataFrame` with: * A ``__comm`` column: the actual rt-app trace task name * A ``__cpu`` column: the CPU on which the task was running at event generation time * A ``__line`` column: the ftrace line numer * A ``__pid`` column: the PID of the task * An ``event`` column: the generated event * A ``phase`` column: the phases counter for each ``__pid``:``__comm`` task * A ``phase_loop`` colum: the phase_loops's counter * A ``thread_loop`` column: the thread_loop's counter The ``event`` column can report these events: * ``start``: the start of the ``__pid``:``__comm`` related event * ``end``: the end of the ``__pid``:``__comm`` related event """ df = self.trace.df_event('userspace@rtapp_loop') df = self._task_filtered(df, task) if wlgen_profile: def get_task_entry(task_name): task = wlgen_profile[task_name] # Build a JSON configuration so that we get the actual number # of phases, after loopification at the task level conf = RTAConf.from_profile({task_name: task}, plat_info=self.trace.plat_info) nr_json_phases = len(conf['tasks'][task_name]['phases']) return (task, nr_json_phases) # Map TaskID to wlgen phases task_map = { task_id: get_task_entry(task_name) for task_name, task_ids in RTA.resolve_trace_task_names( self.trace, wlgen_profile.keys()).items() for task_id in task_ids } class _dict(dict): def __missing__(self, key): raise ValueError(f'Unexpected phase number "{key}"') def f(df): pid, comm = df.name task_id = TaskID(pid=pid, comm=comm) task, nr_json_phases = task_map[task_id] df = df.copy(deep=False) phase_nr = df['thread_loop'] * nr_json_phases + df['phase'] def add_info(get): return phase_nr.map( _dict({ i: get(phase) for i, phase in enumerate(task.phases) })) df['phase'] = add_info(lambda phase: phase.get('name')) df['properties'] = add_info( lambda phase: dict(phase.properties)) return df df = df.groupby(['__pid', '__comm'], observed=True).apply(f) return df
from lisa.wlgen.rta import RTA, Periodic from lisa.datautils import df_filter_task_ids import pandas as pd setup_logging() target = Target.from_one_conf('conf/lisa/qemu_target_default.yml') #target = Target.from_default_conf() rtapp_profile = {} tasks = [] for cpu in range(4): tasks.append("tsk{}-{}".format(cpu, cpu)) rtapp_profile["tsk{}".format(cpu)] = Periodic(duty_cycle_pct=50, duration_s=120) wload = RTA.by_profile(target, "experiment_wload", rtapp_profile) ftrace_coll = FtraceCollector(target, events=["sched_switch"]) trace_path = os.path.join(wload.res_dir, "trace.dat") with ftrace_coll: wload.run() ftrace_coll.get_trace(trace_path) trace = Trace(trace_path, target.plat_info, events=["sched_switch"]) # sched_switch __comm __pid __cpu __line prev_comm prev_pid prev_prio prev_state next_comm next_pid next_prio df = trace.df_events('sched_switch')[['next_pid', 'next_comm', '__cpu']] def analize_task_migration(task_id, ddf): start = ddf.index[0]