Home | History | Annotate | Download | only in tests
      1 #    Copyright 2015-2017 ARM Limited, Google and contributors
      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 import os
     17 import shutil
     18 import sys
     19 import unittest
     20 import utils_tests
     21 import trappy
     22 from trappy.ftrace import GenericFTrace
     23 
     24 class TestCaching(utils_tests.SetupDirectory):
     25     def __init__(self, *args, **kwargs):
     26         super(TestCaching, self).__init__(
     27             [("trace_sched.txt", "trace.txt"),
     28              ("trace_sched.txt", "trace.raw.txt")],
     29             *args,
     30             **kwargs)
     31 
     32     def test_cache_created(self):
     33         """Test cache creation when enabled"""
     34         GenericFTrace.disable_cache = False
     35         trace = trappy.FTrace()
     36 
     37         trace_path = os.path.abspath(trace.trace_path)
     38         trace_dir = os.path.dirname(trace_path)
     39         trace_file = os.path.basename(trace_path)
     40         cache_dir = '.' + trace_file + '.cache'
     41 
     42         self.assertTrue(cache_dir in os.listdir(trace_dir))
     43 
     44     def test_cache_not_created(self):
     45         """Test that cache should not be created when disabled """
     46         GenericFTrace.disable_cache = True
     47         trace = trappy.FTrace()
     48 
     49         trace_path = os.path.abspath(trace.trace_path)
     50         trace_dir = os.path.dirname(trace_path)
     51         trace_file = os.path.basename(trace_path)
     52         cache_dir = '.' + trace_file + '.cache'
     53 
     54         self.assertFalse(cache_dir in os.listdir(trace_dir))
     55 
     56     def test_compare_cached_vs_uncached(self):
     57         """ Test that the cached and uncached traces are same """
     58         # Build the cache, but the actual trace will be parsed
     59         # fresh since this is a first time parse
     60         GenericFTrace.disable_cache = False
     61         uncached_trace = trappy.FTrace()
     62         uncached_dfr = uncached_trace.sched_wakeup.data_frame
     63 
     64         # Now read from previously parsed cache by reusing the path
     65         cached_trace = trappy.FTrace(uncached_trace.trace_path)
     66         cached_dfr = cached_trace.sched_wakeup.data_frame
     67 
     68         # Test whether timestamps are the same:
     69         # The cached/uncached versions of the timestamps are slightly
     70         # different due to floating point precision errors due to converting
     71         # back and forth CSV and DataFrame. For all purposes this is not relevant
     72         # since such rounding doesn't effect the end result.
     73         # Here's an example of the error, the actual normalized time when
     74         # calculated by hand is 0.081489, however following is what's stored
     75         # in the CSV for sched_wakeup events in this trace.
     76         # When converting the index to strings (and also what's in the CSV)
     77         # cached: ['0.0814890000001', '1.981491']
     78         # uncached: ['0.0814890000001', '1.981491']
     79         #
     80         # Keeping index as numpy.float64
     81         # cached: [0.081489000000100009, 1.9814909999999999]
     82         # uncached: [0.081489000000146916, 1.9814909999995507]
     83         #
     84         # To make it possible to test, lets just convert the timestamps to strings
     85         # and compare them below.
     86 
     87         cached_times = [str(r[0]) for r in cached_dfr.iterrows()]
     88         uncached_times = [str(r[0]) for r in uncached_dfr.iterrows()]
     89 
     90         self.assertTrue(cached_times == uncached_times)
     91 
     92         # compare other columns as well
     93         self.assertTrue([r[1].pid for r in cached_dfr.iterrows()] ==
     94                         [r[1].pid for r in uncached_dfr.iterrows()])
     95 
     96         self.assertTrue([r[1].comm for r in cached_dfr.iterrows()] ==
     97                         [r[1].comm for r in uncached_dfr.iterrows()])
     98 
     99         self.assertTrue([r[1].prio for r in cached_dfr.iterrows()] ==
    100                         [r[1].prio for r in uncached_dfr.iterrows()])
    101 
    102     def test_invalid_cache_overwritten(self):
    103         """Test a cache with a bad checksum is overwritten"""
    104         # This is a directory so we can't use the files_to_copy arg of
    105         # SetUpDirectory, just do it ourselves.
    106         cache_path = ".trace.txt.cache"
    107         src = os.path.join(utils_tests.TESTS_DIRECTORY, "trace_sched.txt.cache")
    108         shutil.copytree(src, cache_path)
    109 
    110         md5_path = os.path.join(cache_path, "md5sum")
    111         def read_md5sum():
    112             with open(md5_path) as f:
    113                 return f.read()
    114 
    115         # Change 1 character of the stored checksum
    116         md5sum = read_md5sum()
    117         # Sorry, I guess modifying strings in Python is kind of awkward?
    118         md5sum_inc = "".join(list(md5sum[:-1]) + [chr(ord(md5sum[-1]) + 1)])
    119         with open(md5_path, "w") as f:
    120             f.write(md5sum_inc)
    121 
    122         # Parse a trace, this should delete and overwrite the invalidated cache
    123         GenericFTrace.disable_cache = False
    124         trace = trappy.FTrace()
    125 
    126         # Check that the modified md5sum was overwritten
    127         self.assertNotEqual(read_md5sum(), md5sum_inc,
    128                             "The invalid ftrace cache wasn't overwritten")
    129 
    130     def test_cache_dynamic_events(self):
    131         """Test that caching works if new event parsers have been registered"""
    132 
    133         # Parse the trace to create a cache
    134         GenericFTrace.disable_cache = False
    135         trace1 = trappy.FTrace()
    136 
    137         # Check we're actually testing what we think we are
    138         if hasattr(trace1, 'dynamic_event'):
    139             raise RuntimeError('Test bug: found unexpected event in trace')
    140 
    141         # Now register a new event type, call the constructor again, and check
    142         # that the newly added event (which is not present in the cache) is
    143         # parsed.
    144 
    145         parse_class = trappy.register_dynamic_ftrace("DynamicEvent", "dynamic_test_key")
    146 
    147         trace2 = trappy.FTrace()
    148         self.assertTrue(len(trace2.dynamic_event.data_frame) == 1)
    149 
    150         trappy.unregister_dynamic_ftrace(parse_class)
    151 
    152     def test_cache_normalize_time(self):
    153         """Test that caching doesn't break normalize_time"""
    154         GenericFTrace.disable_cache = False
    155 
    156         # Times in trace_sched.txt
    157         start_time = 6550.018511
    158         first_freq_event_time = 6550.056870
    159 
    160         # Parse without normalizing time
    161         trace1 = trappy.FTrace(events=['cpu_frequency', 'sched_wakeup'],
    162                                normalize_time=False)
    163 
    164         self.assertEqual(trace1.cpu_frequency.data_frame.index[0],
    165                          first_freq_event_time)
    166 
    167         # Parse with normalized time
    168         trace2 = trappy.FTrace(events=['cpu_frequency', 'sched_wakeup'],
    169                                normalize_time=True)
    170 
    171         self.assertEqual(trace2.cpu_frequency.data_frame.index[0],
    172                          first_freq_event_time - start_time)
    173 
    174     def test_cache_window(self):
    175         """Test that caching doesn't break the 'window' parameter"""
    176         GenericFTrace.disable_cache = False
    177 
    178         trace1 = trappy.FTrace(
    179             events=['sched_wakeup'],
    180             window=(0, 1))
    181 
    182         # Check that we're testing what we think we're testing The trace
    183         # contains 2 sched_wakeup events; this window should get rid of one of
    184         # them.
    185         if len(trace1.sched_wakeup.data_frame) != 1:
    186             raise RuntimeError('Test bug: bad sched_wakeup event count')
    187 
    188         # Parse again without the window
    189         trace1 = trappy.FTrace(
    190             events=['sched_wakeup'],
    191             window=(0, None))
    192 
    193         self.assertEqual(len(trace1.sched_wakeup.data_frame), 2)
    194 
    195     def test_cache_delete_single(self):
    196         GenericFTrace.disable_cache = False
    197         trace = trappy.FTrace()
    198 
    199         trace_path = os.path.abspath(trace.trace_path)
    200         trace_dir = os.path.dirname(trace_path)
    201         trace_file = os.path.basename(trace_path)
    202         cache_dir = '.' + trace_file + '.cache'
    203         self.assertEquals(len(os.listdir(cache_dir)), 22)
    204 
    205         os.remove(os.path.join(cache_dir, 'SchedWakeup.csv'))
    206         self.assertEquals(len(os.listdir(cache_dir)), 21)
    207 
    208         # Generate trace again, should regenerate only the missing item
    209         trace = trappy.FTrace()
    210         self.assertEquals(len(os.listdir(cache_dir)), 22)
    211         for c in trace.trace_classes:
    212             if isinstance(c, trace.class_definitions['sched_wakeup']):
    213                 self.assertEquals(c.cached, False)
    214                 continue
    215             self.assertEquals(c.cached, True)
    216