• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#    Copyright 2015-2017 ARM Limited
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14#
15
16
17import matplotlib
18import os
19import pandas as pd
20import re
21import shutil
22import subprocess
23import tempfile
24import time
25import unittest
26
27from test_thermal import BaseTestThermal
28import trappy
29import utils_tests
30
31class TestFTrace(BaseTestThermal):
32    def __init__(self, *args, **kwargs):
33        super(TestFTrace, self).__init__(*args, **kwargs)
34        self.map_label = {"00000000,00000006": "A57", "00000000,00000039": "A53"}
35
36    def test_ftrace_has_all_classes(self):
37        """The FTrace() class has members for all classes"""
38
39        trace = trappy.FTrace()
40
41        for attr in trace.class_definitions.iterkeys():
42            self.assertTrue(hasattr(trace, attr))
43
44    def test_ftrace_has_all_classes_scope_all(self):
45        """The FTrace() class has members for all classes with scope=all"""
46
47        trace = trappy.FTrace(scope="all")
48
49        for attr in trace.class_definitions.iterkeys():
50            self.assertTrue(hasattr(trace, attr))
51
52    def test_ftrace_has_all_classes_scope_thermal(self):
53        """The FTrace() class has only members for thermal classes with scope=thermal"""
54
55        trace = trappy.FTrace(scope="thermal")
56
57        for attr in trace.thermal_classes.iterkeys():
58            self.assertTrue(hasattr(trace, attr))
59
60        for attr in trace.sched_classes.iterkeys():
61            self.assertFalse(hasattr(trace, attr))
62
63    def test_ftrace_has_all_classes_scope_sched(self):
64        """The FTrace() class has only members for sched classes with scope=sched"""
65
66        trace = trappy.FTrace(scope="sched")
67
68        for attr in trace.thermal_classes.iterkeys():
69            self.assertFalse(hasattr(trace, attr))
70
71        for attr in trace.sched_classes.iterkeys():
72            self.assertTrue(hasattr(trace, attr))
73
74    def test_ftrace_has_no_classes_scope_dynamic(self):
75        """The FTrace() class has only dynamically registered classes with scope=custom"""
76
77        trace = trappy.FTrace(scope="custom")
78
79        for attr in trace.thermal_classes.iterkeys():
80            self.assertFalse(hasattr(trace, attr))
81
82        for attr in trace.sched_classes.iterkeys():
83            self.assertFalse(hasattr(trace, attr))
84
85        ftrace_parser = trappy.register_dynamic_ftrace("ADynamicEvent",
86                                                       "a_dynamic_event")
87        trace = trappy.FTrace(scope="custom")
88
89        self.assertTrue(hasattr(trace, "a_dynamic_event"))
90
91        trappy.unregister_dynamic_ftrace(ftrace_parser)
92
93
94    def test_ftrace_doesnt_overwrite_parsed_event(self):
95        """FTrace().add_parsed_event() should not override an event that's already present"""
96        trace = trappy.FTrace()
97        dfr = pd.DataFrame({"temp": [45000, 46724, 45520]},
98                           index=pd.Series([1.020, 1.342, 1.451], name="Time"))
99
100        with self.assertRaises(ValueError):
101            trace.add_parsed_event("sched_switch", dfr)
102
103    def test_fail_if_no_trace_dat(self):
104        """Raise an IOError with the path if there's no trace.dat and trace.txt"""
105        os.remove("trace.txt")
106        self.assertRaises(IOError, trappy.FTrace)
107
108        cwd = os.getcwd()
109
110        try:
111            trappy.FTrace(cwd)
112        except IOError as exception:
113            pass
114
115        self.assertTrue(cwd in str(exception))
116
117    def test_other_directory(self):
118        """FTrace() can grab the trace.dat from other directories"""
119
120        other_random_dir = tempfile.mkdtemp()
121        os.chdir(other_random_dir)
122
123        dfr = trappy.FTrace(self.out_dir).thermal.data_frame
124
125        self.assertTrue(len(dfr) > 0)
126        self.assertEquals(os.getcwd(), other_random_dir)
127
128    def test_ftrace_arbitrary_trace_txt(self):
129        """FTrace() works if the trace is called something other than trace.txt"""
130        arbitrary_trace_name = "my_trace.txt"
131        shutil.move("trace.txt", arbitrary_trace_name)
132
133        dfr = trappy.FTrace(arbitrary_trace_name).thermal.data_frame
134
135        self.assertTrue(len(dfr) > 0)
136        self.assertFalse(os.path.exists("trace.txt"))
137        # As there is no raw trace requested. The mytrace.raw.txt
138        # Should not have been generated
139        self.assertFalse(os.path.exists("mytrace.raw.txt"))
140
141    def test_ftrace_autonormalize_time(self):
142        """FTrace() normalizes by default"""
143
144        trace = trappy.FTrace()
145
146        self.assertEquals(round(trace.thermal.data_frame.index[0], 7), 0)
147
148    def test_ftrace_dont_normalize_time(self):
149        """FTrace() doesn't normalize if asked not to"""
150
151        trace = trappy.FTrace(normalize_time=False)
152
153        self.assertNotEquals(round(trace.thermal.data_frame.index[0], 7), 0)
154
155    def test_ftrace_basetime(self):
156        """Test that basetime calculation is correct"""
157
158        trace = trappy.FTrace(normalize_time=False)
159
160        basetime = trace.thermal.data_frame.index[0]
161
162        self.assertEqual(trace.basetime, basetime)
163
164    def test_ftrace_duration(self):
165        """Test get_duration: normalize_time=False"""
166
167        trace = trappy.FTrace(normalize_time=True)
168
169        duration = trace.thermal_governor.data_frame.index[-1] - trace.thermal.data_frame.index[0]
170
171        self.assertEqual(trace.get_duration(), duration)
172
173    def test_ftrace_duration_not_normalized(self):
174        """Test get_duration: normalize_time=True"""
175
176        trace = trappy.FTrace(normalize_time=False)
177
178        duration = trace.thermal_governor.data_frame.index[-1] - trace.thermal.data_frame.index[0]
179
180        self.assertEqual(trace.get_duration(), duration)
181
182
183    def test_ftrace_normalize_time(self):
184        """FTrace()._normalize_time() works accross all classes"""
185
186        trace = trappy.FTrace(normalize_time=False)
187
188        prev_inpower_basetime = trace.cpu_in_power.data_frame.index[0]
189        prev_inpower_last = trace.cpu_in_power.data_frame.index[-1]
190
191        trace._normalize_time()
192
193        self.assertEquals(round(trace.thermal.data_frame.index[0], 7), 0)
194
195        exp_inpower_first = prev_inpower_basetime - trace.basetime
196        self.assertEquals(round(trace.cpu_in_power.data_frame.index[0] - exp_inpower_first, 7), 0)
197
198        exp_inpower_last = prev_inpower_last - trace.basetime
199        self.assertEquals(round(trace.cpu_in_power.data_frame.index[-1] - exp_inpower_last, 7), 0)
200
201    def test_ftrace_accepts_events(self):
202        """The FTrace class accepts an events parameter with only the parameters interesting for a trace"""
203
204        trace = trappy.FTrace(scope="custom", events=["cdev_update"])
205
206        self.assertGreater(len(trace.cdev_update.data_frame), 1)
207
208        # If you specify events as a string by mistake, trappy does the right thing
209        trace = trappy.FTrace(scope="custom", events="foo")
210        self.assertTrue(hasattr(trace, "foo"))
211
212    def test_ftrace_already_registered_events_are_not_registered_again(self):
213        """FTrace(events="foo") uses class for foo if it is a known class for trappy"""
214        events = ["sched_switch", "sched_load_avg_sg"]
215        trace = trappy.FTrace(scope="custom", events=events)
216
217        self.assertTrue(trace.sched_switch.parse_raw)
218        self.assertEquals(trace.sched_load_avg_sg.pivot, "cpus")
219
220    def test_get_all_freqs_data(self):
221        """Test get_all_freqs_data()"""
222
223        allfreqs = trappy.FTrace().get_all_freqs_data(self.map_label)
224
225        self.assertEquals(allfreqs[1][1]["A53_freq_out"].iloc[3], 850)
226        self.assertEquals(allfreqs[1][1]["A53_freq_in"].iloc[1], 850)
227        self.assertEquals(allfreqs[0][1]["A57_freq_out"].iloc[2], 1100)
228        self.assertTrue("gpu_freq_in" in allfreqs[2][1].columns)
229
230        # Make sure there are no NaNs in the middle of the array
231        self.assertTrue(allfreqs[0][1]["A57_freq_in"].notnull().all())
232
233    def test_apply_callbacks(self):
234        """Test apply_callbacks()"""
235
236        counts = {
237            "cpu_in_power": 0,
238            "cpu_out_power": 0
239        }
240
241        def cpu_in_power_fn(data):
242            counts["cpu_in_power"] += 1
243
244        def cpu_out_power_fn(data):
245            counts["cpu_out_power"] += 1
246
247        fn_map = {
248            "cpu_in_power": cpu_in_power_fn,
249            "cpu_out_power": cpu_out_power_fn
250        }
251        trace = trappy.FTrace()
252
253        trace.apply_callbacks(fn_map)
254
255        self.assertEqual(counts["cpu_in_power"], 134)
256        self.assertEqual(counts["cpu_out_power"], 134)
257
258    def test_plot_freq_hists(self):
259        """Test that plot_freq_hists() doesn't bomb"""
260
261        trace = trappy.FTrace()
262
263        _, axis = matplotlib.pyplot.subplots(nrows=2)
264        trace.plot_freq_hists(self.map_label, axis)
265        matplotlib.pyplot.close('all')
266
267    def test_plot_load(self):
268        """Test that plot_load() doesn't explode"""
269        trace = trappy.FTrace()
270        trace.plot_load(self.map_label, title="Util")
271
272        _, ax = matplotlib.pyplot.subplots()
273        trace.plot_load(self.map_label, ax=ax)
274
275    def test_plot_normalized_load(self):
276        """Test that plot_normalized_load() doesn't explode"""
277
278        trace = trappy.FTrace()
279
280        _, ax = matplotlib.pyplot.subplots()
281        trace.plot_normalized_load(self.map_label, ax=ax)
282
283    def test_plot_allfreqs(self):
284        """Test that plot_allfreqs() doesn't bomb"""
285
286        trace = trappy.FTrace()
287
288        trace.plot_allfreqs(self.map_label)
289        matplotlib.pyplot.close('all')
290
291        _, axis = matplotlib.pyplot.subplots(nrows=2)
292
293        trace.plot_allfreqs(self.map_label, ax=axis)
294        matplotlib.pyplot.close('all')
295
296    def test_plot_allfreqs_with_one_actor(self):
297        """Check that plot_allfreqs() works with one actor"""
298
299        in_data = """     kworker/4:1-397   [004]   720.741349: thermal_power_cpu_get: cpus=00000000,00000006 freq=1400000 raw_cpu_power=189 load={23, 12} power=14
300     kworker/4:1-397   [004]   720.741679: thermal_power_cpu_limit: cpus=00000000,00000006 freq=1400000 cdev_state=1 power=14"""
301
302        with open("trace.txt", "w") as fout:
303            fout.write(in_data)
304
305        trace = trappy.FTrace()
306        map_label = {"00000000,00000006": "A57"}
307        _, axis = matplotlib.pyplot.subplots(nrows=1)
308
309        trace.plot_allfreqs(map_label, ax=[axis])
310        matplotlib.pyplot.close('all')
311
312    def test_trace_metadata(self):
313        """Test if metadata gets populated correctly"""
314
315        expected_metadata = {}
316        expected_metadata["version"] = "6"
317        expected_metadata["cpus"] = "6"
318
319        trace = trappy.FTrace()
320        for key, value in expected_metadata.items():
321            self.assertTrue(hasattr(trace, "_" + key))
322            self.assertEquals(getattr(trace, "_" + key), value)
323
324    def test_missing_metadata(self):
325        """Test if trappy.FTrace() works with a trace missing metadata info"""
326        lines = []
327
328        with open("trace.txt", "r") as fil:
329            lines += fil.readlines()
330            lines = lines[7:]
331            fil.close()
332
333        with open("trace.txt", "w") as fil:
334            fil.write("".join(lines))
335            fil.close()
336
337        trace = trappy.FTrace()
338        self.assertEquals(trace._cpus, None)
339        self.assertEquals(trace._version, None)
340        self.assertTrue(len(trace.thermal.data_frame) > 0)
341
342    def test_ftrace_accepts_window(self):
343        """FTrace class accepts a window parameter"""
344        trace = trappy.FTrace(window=(1.234726, 5.334726))
345        self.assertEquals(trace.thermal.data_frame.iloc[0]["temp"], 68989)
346        self.assertEquals(trace.thermal.data_frame.iloc[-1]["temp"], 69530)
347
348    def test_ftrace_accepts_abs_window(self):
349        """FTrace class accepts an abs_window parameter"""
350        trace = trappy.FTrace(abs_window=(1585, 1589.1))
351        self.assertEquals(trace.thermal.data_frame.iloc[0]["temp"], 68989)
352        self.assertEquals(trace.thermal.data_frame.iloc[-1]["temp"], 69530)
353
354
355@unittest.skipUnless(utils_tests.trace_cmd_installed(),
356                     "trace-cmd not installed")
357class TestFTraceRawDat(utils_tests.SetupDirectory):
358
359    def __init__(self, *args, **kwargs):
360        super(TestFTraceRawDat, self).__init__(
361             [("raw_trace.dat", "trace.dat")],
362             *args,
363             **kwargs)
364
365    def test_raw_dat(self):
366        """Tests an event that relies on raw parsing"""
367
368        trace = trappy.FTrace()
369        self.assertTrue(hasattr(trace, "sched_switch"))
370        self.assertTrue(len(trace.sched_switch.data_frame) > 0)
371        self.assertTrue("prev_comm" in trace.sched_switch.data_frame.columns)
372
373    def test_raw_dat_arb_name(self):
374        """Tests an event that relies on raw parsing with arbitrary .dat file name"""
375
376        arbitrary_name = "my_trace.dat"
377        shutil.move("trace.dat", arbitrary_name)
378
379        trace = trappy.FTrace(arbitrary_name)
380        self.assertTrue(hasattr(trace, "sched_switch"))
381        self.assertTrue(len(trace.sched_switch.data_frame) > 0)
382
383class TestFTraceRawBothTxt(utils_tests.SetupDirectory):
384
385    def __init__(self, *args, **kwargs):
386        super(TestFTraceRawBothTxt, self).__init__(
387             [("raw_trace.txt", "trace.txt"),],
388             *args,
389             **kwargs)
390
391    def test_both_txt_files(self):
392        """test raw parsing for txt files"""
393
394        self.assertFalse(os.path.isfile("trace.dat"))
395        trace = trappy.FTrace()
396        self.assertTrue(hasattr(trace, "sched_switch"))
397        self.assertTrue(len(trace.sched_switch.data_frame) > 0)
398
399    def test_both_txt_arb_name(self):
400        """Test raw parsing for txt files arbitrary name"""
401
402        arbitrary_name = "my_trace.txt"
403
404        shutil.move("trace.txt", arbitrary_name)
405
406        trace = trappy.FTrace(arbitrary_name)
407        self.assertTrue(hasattr(trace, "sched_switch"))
408        self.assertTrue(len(trace.sched_switch.data_frame) > 0)
409
410class TestFTraceSched(utils_tests.SetupDirectory):
411    """Tests using a trace with only sched info and no (or partial) thermal"""
412
413    def __init__(self, *args, **kwargs):
414        super(TestFTraceSched, self).__init__(
415             [("trace_empty.txt", "trace.txt")],
416             *args,
417             **kwargs)
418
419    def test_ftrace_basetime_empty(self):
420        """Test that basetime is 0 if data frame of all data objects is empty"""
421
422        trace = trappy.FTrace(normalize_time=False)
423
424        self.assertEqual(trace.basetime, 0)
425
426    def test_ftrace_unique_but_no_fields(self):
427        """Test with a matching unique but no special fields"""
428        version_parser = trappy.register_dynamic_ftrace("Version", "version")
429
430        # Append invalid line to file
431        with open("trace.txt", "a") as fil:
432            fil.write("version = 6")
433
434        with self.assertRaises(ValueError):
435            trappy.FTrace(scope="custom")
436
437        trappy.unregister_dynamic_ftrace(version_parser)
438
439    def test_ftrace_normalize_some_tracepoints(self):
440        """Test that normalizing time works if not all the tracepoints are in the trace"""
441
442        with open("trace.txt", "a") as fil:
443            fil.write("     kworker/4:1-1219  [004]   508.424826: thermal_temperature:  thermal_zone=exynos-therm id=0 temp_prev=24000 temp=24000")
444
445        trace = trappy.FTrace()
446
447        self.assertEqual(trace.thermal.data_frame.index[0], 0)
448
449@unittest.skipUnless(utils_tests.trace_cmd_installed(),
450                     "trace-cmd not installed")
451class TestTraceDat(utils_tests.SetupDirectory):
452    """Test that trace.dat handling work"""
453    def __init__(self, *args, **kwargs):
454        super(TestTraceDat, self).__init__(
455            [("trace.dat", "trace.dat")],
456            *args, **kwargs)
457
458    def assert_thermal_in_trace(self, fname):
459        """Assert that the thermal event is in the trace
460
461        fname is the trace file, usually "trace.txt" or "trace.raw.txt"
462        """
463
464        found = False
465        with open(fname) as fin:
466            for line in fin:
467                if re.search("thermal", line):
468                    found = True
469                    break
470
471        self.assertTrue(found)
472
473    def test_do_txt_if_not_there(self):
474        """Create trace.txt if it's not there"""
475        self.assertFalse(os.path.isfile("trace.txt"))
476
477        trappy.FTrace()
478
479        self.assert_thermal_in_trace("trace.txt")
480
481    def test_ftrace_arbitrary_trace_dat(self):
482        """FTrace() works if asked to parse a binary trace with a filename other than trace.dat"""
483        arbitrary_trace_name = "my_trace.dat"
484        shutil.move("trace.dat", arbitrary_trace_name)
485
486        dfr = trappy.FTrace(arbitrary_trace_name).thermal.data_frame
487
488        self.assertTrue(os.path.exists("my_trace.txt"))
489        self.assertTrue(len(dfr) > 0)
490        self.assertFalse(os.path.exists("trace.dat"))
491        self.assertFalse(os.path.exists("trace.txt"))
492
493    def test_regenerate_txt_if_outdated(self):
494        """Regenerate the trace.txt if it's older than the trace.dat"""
495
496        trappy.FTrace()
497
498        # Empty the trace.txt
499        with open("trace.txt", "w") as fout:
500            fout.write("")
501
502        # Set access and modified time of trace.txt to 10 seconds ago
503        now = time.time()
504        os.utime("trace.txt", (now - 10, now - 10))
505
506        # touch trace.dat
507        os.utime("trace.dat", None)
508
509        trappy.FTrace()
510
511        self.assert_thermal_in_trace("trace.txt")
512