diff --git a/ipynb/tests/Frequency_Invariance_Test.ipynb b/ipynb/tests/Frequency_Invariance_Test.ipynb index 83af807c0782e8d25303092db3852e46f5aa16aa..83b1d468b04373c919287b463607df65297518c3 100644 --- a/ipynb/tests/Frequency_Invariance_Test.ipynb +++ b/ipynb/tests/Frequency_Invariance_Test.ipynb @@ -117,7 +117,7 @@ } ], "source": [ - "from tests.eas.load_tracking import FreqInvarianceTest\n", + "from tests.eas.load_tracking import FreqInvarianceTest, CpuInvarianceTest, NohzUpdatesTest\n", "\n", "t = FreqInvarianceTest()\n", "print t.__doc__" diff --git a/libs/utils/analysis/cpus_analysis.py b/libs/utils/analysis/cpus_analysis.py index bb4a1f038387fc1854aa837fe7fdd09318083b72..21bc45a39888e1beb3a2658c86a735680793f376 100644 --- a/libs/utils/analysis/cpus_analysis.py +++ b/libs/utils/analysis/cpus_analysis.py @@ -18,6 +18,7 @@ """ CPUs Analysis Module """ import matplotlib.pyplot as plt +import numpy as np import pylab as pl import pandas as pd @@ -63,6 +64,53 @@ class CpusAnalysis(AnalysisModule): ctx_sw_df.index.name = 'cpu' return ctx_sw_df + def _dfg_cpu_load_events(self): + """ + Get a DataFrame with the scheduler's per-CPU load-tracking signals + + Parse the relevant trace event and return a DataFrame with the + scheduler's load tracking update events for each CPU. + + :returns: DataFrame with at least the following columns: + 'cpu', 'load_avg', 'util_avg'. + """ + + # If necessary, rename certain signal names from v5.0 to v5.1 format. + if self._trace.hasEvents('sched_load_avg_cpu'): + df = self._dfg_trace_event('sched_load_avg_cpu') + if 'utilization' in df: + df.rename(columns={'utilization': 'util_avg'}, inplace=True) + df.rename(columns={'load': 'load_avg'}, inplace=True) + + elif self._trace.hasEvents('sched_load_cfs_rq'): + df = self._trace._dfg_trace_event('sched_load_cfs_rq') + df = df.rename(columns={'util': 'util_avg', 'load': 'load_avg'}) + # We're talking about CPU-level evnts so we only care about the + # root_task_group. + df = df[df.path == '/'] + + else: + return None + + # TODO: Remove these additional columns? It doesn't work without + # manually-provided platform data, and it doesn't conceptually belong + # here. + + platform = self._trace.platform + df['cluster'] = np.select( + [df.cpu.isin(platform['clusters']['little'])], + ['LITTLE'], 'big') + # Add a column which represents the max capacity of the smallest + # cluster which can accomodate the task utilization + little_cap = platform['nrg_model']['little']['cpu']['cap_max'] + big_cap = platform['nrg_model']['big']['cpu']['cap_max'] + df['min_cluster_cap'] = df.util_avg.map( + lambda util_avg: big_cap if util_avg > little_cap else little_cap + ) + + return df + + ############################################################################### # Plotting Methods @@ -75,8 +123,8 @@ class CpusAnalysis(AnalysisModule): :param cpus: list of CPUs to be plotted :type cpus: list(int) """ - if not self._trace.hasEvents('sched_load_avg_cpu'): - self._log.warning('Events [sched_load_avg_cpu] not found, ' + if self._dfg_cpu_load_events() is None: + self._log.warning('CPU load tracking events not found, ' 'plot DISABLED!') return @@ -125,7 +173,7 @@ class CpusAnalysis(AnalysisModule): # Add CPU utilization axes.set_title('{0:s}CPU [{1:d}]'.format(label1, cpu)) - df = self._dfg_trace_event('sched_load_avg_cpu') + df = self._dfg_cpu_load_events() df = df[df.cpu == cpu] if len(df): df[['util_avg']].plot(ax=axes, drawstyle='steps-post', diff --git a/libs/utils/analysis/tasks_analysis.py b/libs/utils/analysis/tasks_analysis.py index 46794a03f7c554b2e34bb1e0a8ce47f36317e6dc..da9a1887cc2572915bb63db44f9cb8df5808c7a1 100644 --- a/libs/utils/analysis/tasks_analysis.py +++ b/libs/utils/analysis/tasks_analysis.py @@ -56,15 +56,15 @@ class TasksAnalysis(AnalysisModule): default: capacity of a little cluster :type min_utilization: int """ - if not self._trace.hasEvents('sched_load_avg_task'): - self._log.warning('Events [sched_load_avg_task] not found') + if self._dfg_task_load_events() is None: + self._log.warning('No trace events for task signals, plot DISABLED') return None if min_utilization is None: min_utilization = self._little_cap # Get utilization samples >= min_utilization - df = self._dfg_trace_event('sched_load_avg_task') + df = self._dfg_task_load_events() big_tasks_events = df[df.util_avg > min_utilization] if not len(big_tasks_events): self._log.warning('No tasks with with utilization samples > %d', @@ -182,6 +182,51 @@ class TasksAnalysis(AnalysisModule): return rt_tasks + def _dfg_task_load_events(self): + """ + Get a DataFrame with the scheduler's per-task load-tracking signals + + Parse the relevant trace event and return a DataFrame with the + scheduler's load tracking update events for each task. + + :returns: DataFrame with at least the following columns: + 'comm', 'pid', 'load_avg', 'util_avg'. + """ + if 'sched_load_avg_task' in self._trace.available_events: + df = self._dfg_trace_event('sched_load_avg_task') + if 'utilization' in df: + df.rename(columns={'utilization': 'util_avg'}, inplace=True) + df.rename(columns={'load': 'load_avg'}, inplace=True) + df.rename(columns={'avg_period': 'period_contrib'}, inplace=True) + df.rename(columns={'runnable_avg_sum': 'load_sum'}, inplace=True) + df.rename(columns={'running_avg_sum': 'util_sum'}, inplace=True) + + elif 'sched_load_se' in self._trace.available_events: + df = self._trace._dfg_trace_event('sched_load_se') + df = df.rename(columns={'util': 'util_avg', 'load': 'load_avg'}) + # In sched_load_se, PID shows -1 for task groups. + df = df[df.pid != -1] + else: + return None + + # TODO: Remove these additional columns? It doesn't work without + # manually-provided platform data, and it doesn't conceptually belong + # here. + + platform = self._trace.platform + df['cluster'] = np.select( + [df.cpu.isin(platform['clusters']['little'])], + ['LITTLE'], 'big') + # Add a column which represents the max capacity of the smallest + # cluster which can accomodate the task utilization + little_cap = platform['nrg_model']['little']['cpu']['cap_max'] + big_cap = platform['nrg_model']['big']['cpu']['cap_max'] + df['min_cluster_cap'] = df.util_avg.map( + lambda util_avg: big_cap if util_avg > little_cap else little_cap + ) + + return df + ############################################################################### # Plotting Methods @@ -229,9 +274,8 @@ class TasksAnalysis(AnalysisModule): 'residencies'] # Check for the minimum required signals to be available - if not self._trace.hasEvents('sched_load_avg_task'): - self._log.warning('Events [sched_load_avg_task] not found, ' - 'plot DISABLED!') + if self._dfg_task_load_events() is None: + self._log.warning('No trace events for task signals, plot DISABLED') return # Defined list of tasks to plot @@ -376,7 +420,7 @@ class TasksAnalysis(AnalysisModule): return # Get the list of events for all big frequent tasks - df = self._dfg_trace_event('sched_load_avg_task') + df = self._dfg_task_load_events() big_frequent_tasks_events = df[df.pid.isin(big_frequent_task_pids)] # Define axes for side-by-side plottings @@ -533,13 +577,16 @@ class TasksAnalysis(AnalysisModule): :type big_cluster: bool """ - if not self._trace.hasEvents('sched_load_avg_task'): - self._log.warning('Events [sched_load_avg_task] not found') - return if not self._trace.hasEvents('cpu_frequency'): self._log.warning('Events [cpu_frequency] not found') return + # Get all utilization update events + df = self._dfg_task_load_events() + if df is None: + self._log.warning('No trace events for task signals, plot DISABLED') + return + if big_cluster: cluster_correct = 'big' cpus = self._big_cpus @@ -547,9 +594,6 @@ class TasksAnalysis(AnalysisModule): cluster_correct = 'LITTLE' cpus = self._little_cpus - # Get all utilization update events - df = self._dfg_trace_event('sched_load_avg_task') - # Keep events of defined big tasks big_task_pids = self._dfg_top_big_tasks( min_samples, min_utilization) @@ -635,7 +679,7 @@ class TasksAnalysis(AnalysisModule): :type is_last: bool """ # Get dataframe for the required task - util_df = self._dfg_trace_event('sched_load_avg_task') + util_df = self._dfg_task_load_events() # Plot load and util signals_to_plot = set(signals).difference({'boosted_util'}) @@ -698,7 +742,7 @@ class TasksAnalysis(AnalysisModule): :param is_last: if True this is the last plot :type is_last: bool """ - util_df = self._dfg_trace_event('sched_load_avg_task') + util_df = self._dfg_task_load_events() data = util_df[util_df.pid == tid][['cluster', 'cpu']] for ccolor, clabel in zip('gr', ['LITTLE', 'big']): cdata = data[data.cluster == clabel] @@ -733,6 +777,11 @@ class TasksAnalysis(AnalysisModule): :param signals: signals to be plot :param signals: list(str) """ + if not self._trace.hasEvents('sched_load_avg_task'): + self._log.warning( + 'No sched_load_avg_task events, skipping PELT plot') + return + util_df = self._dfg_trace_event('sched_load_avg_task') data = util_df[util_df.pid == tid][['load_sum', 'util_sum', diff --git a/libs/utils/trace.py b/libs/utils/trace.py index 80b6133d7fa959dea58dc3268ce677eff4807e72..ad7dc649898aba7bdc53a03495800baa1788e0c2 100644 --- a/libs/utils/trace.py +++ b/libs/utils/trace.py @@ -251,8 +251,6 @@ class Trace(object): # Setup internal data reference to interesting events/dataframes - self._sanitize_SchedLoadAvgCpu() - self._sanitize_SchedLoadAvgTask() self._sanitize_SchedCpuCapacity() self._sanitize_SchedBoostCpu() self._sanitize_SchedBoostTask() @@ -508,41 +506,6 @@ class Trace(object): [df.cpu.isin(self.platform['clusters']['little'])], [tip_lcap], tip_bcap) - def _sanitize_SchedLoadAvgCpu(self): - """ - If necessary, rename certain signal names from v5.0 to v5.1 format. - """ - if not self.hasEvents('sched_load_avg_cpu'): - return - df = self._dfg_trace_event('sched_load_avg_cpu') - if 'utilization' in df: - df.rename(columns={'utilization': 'util_avg'}, inplace=True) - df.rename(columns={'load': 'load_avg'}, inplace=True) - - def _sanitize_SchedLoadAvgTask(self): - """ - If necessary, rename certain signal names from v5.0 to v5.1 format. - """ - if not self.hasEvents('sched_load_avg_task'): - return - df = self._dfg_trace_event('sched_load_avg_task') - if 'utilization' in df: - df.rename(columns={'utilization': 'util_avg'}, inplace=True) - df.rename(columns={'load': 'load_avg'}, inplace=True) - df.rename(columns={'avg_period': 'period_contrib'}, inplace=True) - df.rename(columns={'runnable_avg_sum': 'load_sum'}, inplace=True) - df.rename(columns={'running_avg_sum': 'util_sum'}, inplace=True) - df['cluster'] = np.select( - [df.cpu.isin(self.platform['clusters']['little'])], - ['LITTLE'], 'big') - # Add a column which represents the max capacity of the smallest - # clustre which can accomodate the task utilization - little_cap = self.platform['nrg_model']['little']['cpu']['cap_max'] - big_cap = self.platform['nrg_model']['big']['cpu']['cap_max'] - df['min_cluster_cap'] = df.util_avg.map( - lambda util_avg: big_cap if util_avg > little_cap else little_cap - ) - def _sanitize_SchedBoostCpu(self): """ Add a boosted utilization signal as the sum of utilization and margin. diff --git a/tests/eas/load_tracking.py b/tests/eas/load_tracking.py index 989c57137d73780f0d06468a6826c7ea0abd2003..cea92a9972740b463f80e15a52e87a4fad4b890c 100644 --- a/tests/eas/load_tracking.py +++ b/tests/eas/load_tracking.py @@ -37,8 +37,8 @@ class _LoadTrackingBase(LisaTest): 'sched_switch', 'sched_load_avg_task', 'sched_load_avg_cpu', - 'sched_pelt_se', - 'sched_load_se' + 'sched_load_se', + 'sched_load_cfs_rq' ], }, # cgroups required by freeze_userspace flag @@ -58,6 +58,7 @@ class _LoadTrackingBase(LisaTest): def setUpClass(cls, *args, **kwargs): super(_LoadTrackingBase, cls).runExperiments(*args, **kwargs) +class _InvarianceBase(LisaTest): @classmethod def get_wload(cls, cpu): """ @@ -323,3 +324,84 @@ class CpuInvarianceTest(_LoadTrackingBase): Test that the mean of the util_avg signal matched the expected value """ return self._test_signal(experiment, tasks, 'util_avg') + +class NohzUpdatesTest(_LoadTrackingBase): + """ + Test that load tracking signals are updated while CPUs are idle + + This test runs a workload that creates load and then goes suddenly idle. It + then collects the load-tracking signals for this workload and asserts that + after a certain time the load was decayed, despite no tasks (in theory) + running on the CPU. + """ + PELT_HALF_LIFE_MS = 32 + + util_pct = 60 + + @classmethod + def _getExperimentsConf(cls, te): + # Run on highest-capacity CPU + cls.max_cap_cpu = max(range(te.target.number_of_cpus), + key=lambda c: cls._get_cpu_capacity(te, c)) + + return { + 'wloads': { + 'nohz_wload' : { + 'type' : 'rt-app', + 'conf' : { + 'class' : 'profile', + 'params' : { + 'wmig' : { + 'kind' : 'Step', + 'params' : { + "period_ms" : 16, + 'start_pct': cls.util_pct, + 'end_pct': 0, + 'time_s': 2, + 'cpus': [cls.max_cap_cpu], + }, + }, + }, + }, + }, + }, + 'confs': [{'tag': 'conf1', 'flags': ['ftrace']}] + } + + @experiment_test + def test_signals_decayed(self, experiment, tasks): + trace = self.get_trace(experiment) + [comm] = tasks + [pid] = trace.getTaskByName(comm) + + # Get last time the workload ran + sw = trace.data_frame.trace_event('sched_switch') + # The workload will have an activation at the end of the 0% step, so we + # want the _penultimate_ switch-out event + end_time = sw[sw.prev_pid == pid].index[-2] + num_half_lives = 3 + decayed_time = end_time + num_half_lives * self.PELT_HALF_LIFE_MS + + # Get the util_avg signal at the times we're interested in + df = trace.data_frame.task_load_events() + util = df[df.pid == pid].util_avg + util = util.reindex([end_time, decayed_time], method='ffill') + + # Sanity check util value at the end of the 60% step + end_util = util[end_time] + cpu_cap = self._get_cpu_capacity(self.te, self.max_cap_cpu) + exp_end_util = cpu_cap * (self.util_pct / 100.) + msg = 'Expected util {} at end of load step, saw {}. Maybe test bug'.format( + exp_end_util, end_util + ) + self.assertAlmostEqual(end_util, exp_end_util, + delta=0.05 * exp_end_util, msg=msg) + + # Test that we were decayed when idle + decayed_util = util[decayed_time] + exp_decayed_util = end_util * (0.5 ** num_half_lives) + msg = 'Expected util {} after {} half lives, saw {}. Maybe test bug'.format( + exp_end_util, num_half_lives, end_util + ) + self.assertAlmostEqual(end_util, exp_end_util, + delta=0.05 * exp_decayed_util, msg=msg) diff --git a/tests/lisa/__init__.py b/tests/lisa/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..19e14f33a4ea8d6d51e8f5960df470ff1898d069 --- /dev/null +++ b/tests/lisa/__init__.py @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (C) 2017, ARM Limited and contributors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import matplotlib +# Prevent matplotlib from trying to connect to X11 server, for headless testing. +# Must be done before importing matplotlib.pyplot or pylab +matplotlib.use('Agg') diff --git a/tests/lisa/example_traces/.gitignore b/tests/lisa/example_traces/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..877a26e3e5d91fe45e065da56a3a114cdc0d945d --- /dev/null +++ b/tests/lisa/example_traces/.gitignore @@ -0,0 +1,4 @@ +# Text versions of traces, created by Trappy +*.txt +# Plot images creating by testing plotters +*.png diff --git a/tests/lisa/example_traces/sched_load/platform.json b/tests/lisa/example_traces/sched_load/platform.json new file mode 100644 index 0000000000000000000000000000000000000000..d2c02c7a20a4b9a126d20fbd9c6a1d1ff334121b --- /dev/null +++ b/tests/lisa/example_traces/sched_load/platform.json @@ -0,0 +1,79 @@ +{ + "abi": "arm64", + "clusters": { + "big": [ + 1, + 2 + ], + "little": [ + 0, + 3, + 4, + 5 + ] + }, + "cpus_count": 6, + "freqs": { + "big": [ + 450000, + 625000, + 800000, + 950000, + 1100000 + ], + "little": [ + 450000, + 575000, + 700000, + 775000, + 850000 + ] + }, + "kernel": { + "major": 11, + "minor": 0, + "parts": [ + 4, + 11, + 0 + ], + "rc": 6, + "release": "4.11.0-rc6-00092-g9cc3141d9e4f-dirty", + "sha1": "9cc3141d9e4f", + "version": "58 SMP PREEMPT Wed May 24 18:37:50 BST 2017", + "version_number": 4 + }, + "nrg_model": { + "big": { + "cluster": { + "nrg_max": 64 + }, + "cpu": { + "cap_max": 1024, + "nrg_max": 616 + } + }, + "little": { + "cluster": { + "nrg_max": 57 + }, + "cpu": { + "cap_max": 447, + "nrg_max": 93 + } + } + }, + "os": "linux", + "topology": [ + [ + 0, + 3, + 4, + 5 + ], + [ + 1, + 2 + ] + ] +} \ No newline at end of file diff --git a/tests/lisa/example_traces/sched_load/trace.dat b/tests/lisa/example_traces/sched_load/trace.dat new file mode 100644 index 0000000000000000000000000000000000000000..0456cd04187bc53f7cc57ca99716b0234478c139 Binary files /dev/null and b/tests/lisa/example_traces/sched_load/trace.dat differ diff --git a/tests/lisa/example_traces/sched_load_avg/platform.json b/tests/lisa/example_traces/sched_load_avg/platform.json new file mode 100644 index 0000000000000000000000000000000000000000..80e06149bb0f004c999a48dd1c0cf7f3c1b665b2 --- /dev/null +++ b/tests/lisa/example_traces/sched_load_avg/platform.json @@ -0,0 +1,108 @@ +{ + "abi": "arm64", + "clusters": { + "big": [ + 2, + 3 + ], + "little": [ + 0, + 1 + ] + }, + "cpus_count": 4, + "freqs": { + "big": [ + 307200, + 384000, + 460800, + 537600, + 614400, + 691200, + 748800, + 825600, + 902400, + 979200, + 1056000, + 1132800, + 1209600, + 1286400, + 1363200, + 1440000, + 1516800, + 1593600, + 1670400, + 1747200, + 1824000, + 1900800, + 1977600, + 2054400, + 2150400 + ], + "little": [ + 307200, + 384000, + 460800, + 537600, + 614400, + 691200, + 768000, + 844800, + 902400, + 979200, + 1056000, + 1132800, + 1209600, + 1286400, + 1363200, + 1440000, + 1516800, + 1593600 + ] + }, + "kernel": { + "major": 18, + "minor": 31, + "parts": [ + 3, + 18, + 31 + ], + "rc": null, + "release": "3.18.31-gbd96fbf", + "sha1": "bd96fbf", + "version": "1 SMP PREEMPT Mon Nov 7 20:29:14 UTC 2016", + "version_number": 3 + }, + "nrg_model": { + "big": { + "cluster": { + "nrg_max": 96 + }, + "cpu": { + "cap_max": 1024, + "nrg_max": 1715 + } + }, + "little": { + "cluster": { + "nrg_max": 52 + }, + "cpu": { + "cap_max": 763, + "nrg_max": 925 + } + } + }, + "os": "android", + "topology": [ + [ + 0, + 1 + ], + [ + 2, + 3 + ] + ] +} \ No newline at end of file diff --git a/tests/lisa/example_traces/sched_load_avg/trace.dat b/tests/lisa/example_traces/sched_load_avg/trace.dat new file mode 100644 index 0000000000000000000000000000000000000000..9583ea8ad8ef4ef3cfe9f324dea6d17ac1e776d2 Binary files /dev/null and b/tests/lisa/example_traces/sched_load_avg/trace.dat differ diff --git a/tests/lisa/test_trace.py b/tests/lisa/test_trace.py index 30885e91c5f7b846fe3c5a5fc6a1544aa4c1902b..0f2e0eb6ee9611a05da20ca89edbe0d14e92c96c 100644 --- a/tests/lisa/test_trace.py +++ b/tests/lisa/test_trace.py @@ -21,24 +21,27 @@ from unittest import TestCase from trace import Trace -class TestTrace(TestCase): - """Smoke tests for LISA's Trace class""" - traces_dir = os.path.join(os.path.dirname(__file__), 'traces') - events = [ - 'sched_switch', - ] +class TraceBase(TestCase): + """Base class for tests for Trace class""" + + events = ['sched_switch', 'sched_load_se', 'sched_load_avg_task', + 'sched_load_cfs_rq', 'sched_load_avg_cpu'] def __init__(self, *args, **kwargs): - super(TestTrace, self).__init__(*args, **kwargs) + super(TraceBase, self).__init__(*args, **kwargs) self.test_trace = os.path.join(self.traces_dir, 'test_trace.txt') with open(os.path.join(self.traces_dir, 'platform.json')) as f: self.platform = json.load(f) - trace_path = os.path.join(self.traces_dir, 'trace.txt') - self.trace = Trace(self.platform, trace_path, self.events) + self.trace = Trace(self.platform, self.traces_dir, self.events) + +class TestTrace(TraceBase): + """Smoke tests for LISA's Trace class""" + + traces_dir = os.path.join(os.path.dirname(__file__), 'traces') def test_getTaskByName(self): """TestTrace: getTaskByName() returns the list of PIDs for all tasks with the specified name""" @@ -78,3 +81,66 @@ class TestTrace(TestCase): self.assertEqual(trace.getTaskByName('father'), [1234]) os.remove(self.test_trace) + + +class TaskSignalsBase(TraceBase): + """Test getting scheduler task signals from traces""" + + def _test_tasks_dfs(self): + """Helper for smoke testing _dfg methods in tasks_analysis""" + df = self.trace.data_frame.task_load_events() + for column in ['comm', 'pid', 'load_avg', 'util_avg', 'cluster', 'cpu']: + msg = 'Task signals parsed from {} missing {} column'.format( + self.trace.data_dir, column) + self.assertIn(column, df, msg=msg) + + df = self.trace.data_frame.top_big_tasks(min_samples=1) + for column in ['samples', 'comm']: + msg = 'Big tasks parsed from {} missing {} column'.format( + self.trace.data_dir, column) + self.assertIn(column, df, msg=msg) + + # Pick an arbitrary PID to try plotting signals for. + # Call plotTasks - although we won't check the results we can just check + # that things aren't totally borken. + pid = self.trace.getTasks().keys()[-1] + self.trace.analysis.tasks.plotTasks(tasks=[pid]) + + def _test_cpus_dfs(self): + """Helper for smoke testing _dfg methods in cpus_analysis""" + df = self.trace.data_frame.cpu_load_events() + for column in ['cpu', 'load_avg', 'util_avg']: + msg = 'CPU signals parsed from {} missing {} column'.format( + self.trace.data_dir, column) + self.assertIn(column, df, msg=msg) + + # Call plotCPU - although we won't check the results we can just check + # that things aren't totally borken. + self.trace.analysis.cpus.plotCPU() + +class TestTraceSchedLoad(TaskSignalsBase): + """Test parsing sched_load_* events""" + + traces_dir = os.path.join(os.path.dirname(__file__), 'traces', 'sched_load') + + def test_sched_load_signals(self): + """Test parsing sched_load_se events from EAS upstream integration""" + self._test_tasks_dfs() + + def test_sched_load_signals(self): + """Test parsing sched_load_cfs_rq events from EAS upstream integration""" + self._test_cpus_dfs() + +class TestTraceSchedLoadAvg(TaskSignalsBase): + """Test parsing sched_load_avg_* events""" + + traces_dir = os.path.join(os.path.dirname(__file__), + 'traces', 'sched_load_avg') + + def test_sched_load_avg_signals(self): + """Test parsing sched_load_avg_task events from EAS1.2""" + self._test_tasks_dfs() + + def test_sched_load_avg_signals(self): + """Test parsing sched_load_avg_cpu events from EAS1.2""" + self._test_cpus_dfs()