1# SPDX-License-Identifier: Apache-2.0 2# 3# Copyright (C) 2017, ARM Limited and contributors. 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); you may 6# not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18import json 19import os 20from unittest import TestCase 21 22from trace import Trace 23 24class TestTrace(TestCase): 25 """Smoke tests for LISA's Trace class""" 26 27 traces_dir = os.path.join(os.path.dirname(__file__), 'traces') 28 events = [ 29 'sched_switch', 30 'sched_overutilized', 31 'cpu_idle', 32 ] 33 34 def __init__(self, *args, **kwargs): 35 super(TestTrace, self).__init__(*args, **kwargs) 36 37 self.test_trace = os.path.join(self.traces_dir, 'test_trace.txt') 38 39 self.platform = self._get_platform() 40 41 self.trace_path = os.path.join(self.traces_dir, 'trace.txt') 42 self.trace = Trace(self.platform, self.trace_path, self.events) 43 44 def make_trace(self, in_data): 45 with open(self.test_trace, "w") as fout: 46 fout.write(in_data) 47 48 return Trace(self.platform, self.test_trace, self.events, 49 normalize_time=False) 50 51 def _get_platform(self): 52 with open(os.path.join(self.traces_dir, 'platform.json')) as f: 53 return json.load(f) 54 55 def test_getTaskByName(self): 56 """TestTrace: getTaskByName() returns the list of PIDs for all tasks with the specified name""" 57 for name, pids in [('watchdog/0', [12]), 58 ('sh', [1642, 1702, 1717, 1718]), 59 ('NOT_A_TASK', [])]: 60 self.assertEqual(self.trace.getTaskByName(name), pids) 61 62 def test_getTaskByPid(self): 63 """TestTrace: getTaskByPid() returns the name of the task with the specified PID""" 64 for pid, names in [(15, 'watchdog/1'), 65 (1639, 'sshd'), 66 (987654321, None)]: 67 self.assertEqual(self.trace.getTaskByPid(pid), names) 68 69 def test_getTasks(self): 70 """TestTrace: getTasks() returns a dictionary mapping PIDs to a single task name""" 71 tasks_dict = self.trace.getTasks() 72 for pid, name in [(1, 'init'), 73 (9, 'rcu_sched'), 74 (1383, 'jbd2/sda2-8')]: 75 self.assertEqual(tasks_dict[pid], name) 76 77 def test_setTaskName(self): 78 """TestTrace: getTaskBy{Pid,Name}() properly track tasks renaming""" 79 in_data = """ 80 father-1234 [002] 18765.018235: sched_switch: prev_comm=father prev_pid=1234 prev_prio=120 prev_state=0 next_comm=father next_pid=5678 next_prio=120 81 child-5678 [002] 18766.018236: sched_switch: prev_comm=child prev_pid=5678 prev_prio=120 prev_state=1 next_comm=sh next_pid=3367 next_prio=120 82 """ 83 trace = self.make_trace(in_data) 84 85 self.assertEqual(trace.getTaskByPid(1234), 'father') 86 self.assertEqual(trace.getTaskByPid(5678), 'child') 87 self.assertEqual(trace.getTaskByName('father'), [1234]) 88 89 os.remove(self.test_trace) 90 91 def test_time_range(self): 92 """ 93 TestTrace: time_range is the duration of the trace 94 """ 95 expected_duration = 6.676497 96 97 trace = Trace(self.platform, self.trace_path, 98 self.events, normalize_time=False 99 ) 100 101 self.assertAlmostEqual(trace.time_range, expected_duration, places=6) 102 103 def test_time_range_window(self): 104 """ 105 TestTrace: time_range is the duration of the trace in the given window 106 """ 107 expected_duration = 4.0 108 109 trace = Trace(self.platform, self.trace_path, 110 self.events, normalize_time=False, 111 window=(76.402065, 80.402065) 112 ) 113 114 self.assertAlmostEqual(trace.time_range, expected_duration, places=6) 115 116 def test_overutilized_time(self): 117 """ 118 TestTrace: overutilized_time is the total time spent while system was overutilized 119 """ 120 events = [ 121 76.402065, 122 80.402065, 123 82.001337 124 ] 125 126 trace_end = self.trace.ftrace.basetime + self.trace.ftrace.get_duration() 127 # Last event should be extended to the trace's end 128 expected_time = (events[1] - events[0]) + (trace_end - events[2]) 129 130 self.assertAlmostEqual(self.trace.overutilized_time, expected_time, places=6) 131 132 def test_plotCPUIdleStateResidency(self): 133 """ 134 Test that plotCPUIdleStateResidency doesn't crash 135 """ 136 in_data = """ 137 foo-1 [000] 0.01: cpu_idle: state=0 cpu_id=0 138 foo-1 [000] 0.02: cpu_idle: state=-1 cpu_id=0 139 bar-2 [000] 0.03: cpu_idle: state=0 cpu_id=1 140 bar-2 [000] 0.04: cpu_idle: state=-1 cpu_id=1 141 baz-3 [000] 0.05: cpu_idle: state=0 cpu_id=2 142 baz-3 [000] 0.06: cpu_idle: state=-1 cpu_id=2 143 bam-4 [000] 0.07: cpu_idle: state=0 cpu_id=3 144 bam-4 [000] 0.08: cpu_idle: state=-1 cpu_id=3 145 child-5678 [002] 18765.018235: sched_switch: prev_comm=child prev_pid=5678 prev_prio=120 prev_state=1 next_comm=father next_pid=5678 next_prio=120 146 """ 147 trace = self.make_trace(in_data) 148 149 trace.analysis.idle.plotCPUIdleStateResidency() 150 151 def test_deriving_cpus_count(self): 152 """Test that Trace derives cpus_count if it isn't provided""" 153 if self.platform: 154 del self.platform['cpus_count'] 155 156 in_data = """ 157 father-1234 [000] 18765.018235: sched_switch: prev_comm=father prev_pid=1234 prev_prio=120 prev_state=0 next_comm=father next_pid=5678 next_prio=120 158 child-5678 [002] 18765.018235: sched_switch: prev_comm=child prev_pid=5678 prev_prio=120 prev_state=1 next_comm=father next_pid=5678 next_prio=120 159 """ 160 161 trace = self.make_trace(in_data) 162 163 self.assertEqual(trace.platform['cpus_count'], 3) 164 165 def test_dfg_cpu_wakeups(self): 166 """ 167 Test the cpu_wakeups DataFrame getter 168 """ 169 trace = self.make_trace(""" 170 <idle>-0 [004] 519.021928: cpu_idle: state=4294967295 cpu_id=4 171 <idle>-0 [004] 519.022147: cpu_idle: state=0 cpu_id=4 172 <idle>-0 [004] 519.022641: cpu_idle: state=4294967295 cpu_id=4 173 <idle>-0 [001] 519.022642: cpu_idle: state=4294967295 cpu_id=1 174 <idle>-0 [002] 519.022643: cpu_idle: state=4294967295 cpu_id=2 175 <idle>-0 [001] 519.022788: cpu_idle: state=0 cpu_id=1 176 <idle>-0 [002] 519.022831: cpu_idle: state=2 cpu_id=2 177 <idle>-0 [003] 519.022867: cpu_idle: state=4294967295 cpu_id=3 178 <idle>-0 [003] 519.023045: cpu_idle: state=2 cpu_id=3 179 <idle>-0 [004] 519.023080: cpu_idle: state=1 cpu_id=4 180 """) 181 182 df = trace.data_frame.cpu_wakeups() 183 184 exp_index=[519.021928, 519.022641, 519.022642, 519.022643, 519.022867] 185 exp_cpus= [ 4, 4, 1, 2, 3] 186 self.assertListEqual(df.index.tolist(), exp_index) 187 self.assertListEqual(df.cpu.tolist(), exp_cpus) 188 189 df = trace.data_frame.cpu_wakeups([2]) 190 191 self.assertListEqual(df.index.tolist(), [519.022643]) 192 self.assertListEqual(df.cpu.tolist(), [2]) 193 194class TestTraceNoClusterData(TestTrace): 195 """ 196 Test Trace without cluster data 197 198 Inherits from TestTrace, so all the tests are run again but with 199 no cluster info the platform dict. 200 """ 201 def _get_platform(self): 202 platform = super(TestTraceNoClusterData, self)._get_platform() 203 del platform['clusters'] 204 return platform 205 206class TestTraceNoPlatform(TestTrace): 207 """ 208 Test Trace with platform=none 209 210 Inherits from TestTrace, so all the tests are run again but with 211 platform=None 212 """ 213 def _get_platform(self): 214 return None 215