aboutsummaryrefslogtreecommitdiff
path: root/tests/test_caching.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_caching.py')
-rw-r--r--tests/test_caching.py215
1 files changed, 215 insertions, 0 deletions
diff --git a/tests/test_caching.py b/tests/test_caching.py
new file mode 100644
index 0000000..9fcd33a
--- /dev/null
+++ b/tests/test_caching.py
@@ -0,0 +1,215 @@
+# Copyright 2015-2017 ARM Limited, Google 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 os
+import shutil
+import sys
+import unittest
+import utils_tests
+import trappy
+from trappy.ftrace import GenericFTrace
+
+class TestCaching(utils_tests.SetupDirectory):
+ def __init__(self, *args, **kwargs):
+ super(TestCaching, self).__init__(
+ [("trace_sched.txt", "trace.txt"),
+ ("trace_sched.txt", "trace.raw.txt")],
+ *args,
+ **kwargs)
+
+ def test_cache_created(self):
+ """Test cache creation when enabled"""
+ GenericFTrace.disable_cache = False
+ trace = trappy.FTrace()
+
+ trace_path = os.path.abspath(trace.trace_path)
+ trace_dir = os.path.dirname(trace_path)
+ trace_file = os.path.basename(trace_path)
+ cache_dir = '.' + trace_file + '.cache'
+
+ self.assertTrue(cache_dir in os.listdir(trace_dir))
+
+ def test_cache_not_created(self):
+ """Test that cache should not be created when disabled """
+ GenericFTrace.disable_cache = True
+ trace = trappy.FTrace()
+
+ trace_path = os.path.abspath(trace.trace_path)
+ trace_dir = os.path.dirname(trace_path)
+ trace_file = os.path.basename(trace_path)
+ cache_dir = '.' + trace_file + '.cache'
+
+ self.assertFalse(cache_dir in os.listdir(trace_dir))
+
+ def test_compare_cached_vs_uncached(self):
+ """ Test that the cached and uncached traces are same """
+ # Build the cache, but the actual trace will be parsed
+ # fresh since this is a first time parse
+ GenericFTrace.disable_cache = False
+ uncached_trace = trappy.FTrace()
+ uncached_dfr = uncached_trace.sched_wakeup.data_frame
+
+ # Now read from previously parsed cache by reusing the path
+ cached_trace = trappy.FTrace(uncached_trace.trace_path)
+ cached_dfr = cached_trace.sched_wakeup.data_frame
+
+ # Test whether timestamps are the same:
+ # The cached/uncached versions of the timestamps are slightly
+ # different due to floating point precision errors due to converting
+ # back and forth CSV and DataFrame. For all purposes this is not relevant
+ # since such rounding doesn't effect the end result.
+ # Here's an example of the error, the actual normalized time when
+ # calculated by hand is 0.081489, however following is what's stored
+ # in the CSV for sched_wakeup events in this trace.
+ # When converting the index to strings (and also what's in the CSV)
+ # cached: ['0.0814890000001', '1.981491']
+ # uncached: ['0.0814890000001', '1.981491']
+ #
+ # Keeping index as numpy.float64
+ # cached: [0.081489000000100009, 1.9814909999999999]
+ # uncached: [0.081489000000146916, 1.9814909999995507]
+ #
+ # To make it possible to test, lets just convert the timestamps to strings
+ # and compare them below.
+
+ cached_times = [str(r[0]) for r in cached_dfr.iterrows()]
+ uncached_times = [str(r[0]) for r in uncached_dfr.iterrows()]
+
+ self.assertTrue(cached_times == uncached_times)
+
+ # compare other columns as well
+ self.assertTrue([r[1].pid for r in cached_dfr.iterrows()] ==
+ [r[1].pid for r in uncached_dfr.iterrows()])
+
+ self.assertTrue([r[1].comm for r in cached_dfr.iterrows()] ==
+ [r[1].comm for r in uncached_dfr.iterrows()])
+
+ self.assertTrue([r[1].prio for r in cached_dfr.iterrows()] ==
+ [r[1].prio for r in uncached_dfr.iterrows()])
+
+ def test_invalid_cache_overwritten(self):
+ """Test a cache with a bad checksum is overwritten"""
+ # This is a directory so we can't use the files_to_copy arg of
+ # SetUpDirectory, just do it ourselves.
+ cache_path = ".trace.txt.cache"
+ src = os.path.join(utils_tests.TESTS_DIRECTORY, "trace_sched.txt.cache")
+ shutil.copytree(src, cache_path)
+
+ md5_path = os.path.join(cache_path, "md5sum")
+ def read_md5sum():
+ with open(md5_path) as f:
+ return f.read()
+
+ # Change 1 character of the stored checksum
+ md5sum = read_md5sum()
+ # Sorry, I guess modifying strings in Python is kind of awkward?
+ md5sum_inc = "".join(list(md5sum[:-1]) + [chr(ord(md5sum[-1]) + 1)])
+ with open(md5_path, "w") as f:
+ f.write(md5sum_inc)
+
+ # Parse a trace, this should delete and overwrite the invalidated cache
+ GenericFTrace.disable_cache = False
+ trace = trappy.FTrace()
+
+ # Check that the modified md5sum was overwritten
+ self.assertNotEqual(read_md5sum(), md5sum_inc,
+ "The invalid ftrace cache wasn't overwritten")
+
+ def test_cache_dynamic_events(self):
+ """Test that caching works if new event parsers have been registered"""
+
+ # Parse the trace to create a cache
+ GenericFTrace.disable_cache = False
+ trace1 = trappy.FTrace()
+
+ # Check we're actually testing what we think we are
+ if hasattr(trace1, 'dynamic_event'):
+ raise RuntimeError('Test bug: found unexpected event in trace')
+
+ # Now register a new event type, call the constructor again, and check
+ # that the newly added event (which is not present in the cache) is
+ # parsed.
+
+ parse_class = trappy.register_dynamic_ftrace("DynamicEvent", "dynamic_test_key")
+
+ trace2 = trappy.FTrace()
+ self.assertTrue(len(trace2.dynamic_event.data_frame) == 1)
+
+ trappy.unregister_dynamic_ftrace(parse_class)
+
+ def test_cache_normalize_time(self):
+ """Test that caching doesn't break normalize_time"""
+ GenericFTrace.disable_cache = False
+
+ # Times in trace_sched.txt
+ start_time = 6550.018511
+ first_freq_event_time = 6550.056870
+
+ # Parse without normalizing time
+ trace1 = trappy.FTrace(events=['cpu_frequency', 'sched_wakeup'],
+ normalize_time=False)
+
+ self.assertEqual(trace1.cpu_frequency.data_frame.index[0],
+ first_freq_event_time)
+
+ # Parse with normalized time
+ trace2 = trappy.FTrace(events=['cpu_frequency', 'sched_wakeup'],
+ normalize_time=True)
+
+ self.assertEqual(trace2.cpu_frequency.data_frame.index[0],
+ first_freq_event_time - start_time)
+
+ def test_cache_window(self):
+ """Test that caching doesn't break the 'window' parameter"""
+ GenericFTrace.disable_cache = False
+
+ trace1 = trappy.FTrace(
+ events=['sched_wakeup'],
+ window=(0, 1))
+
+ # Check that we're testing what we think we're testing The trace
+ # contains 2 sched_wakeup events; this window should get rid of one of
+ # them.
+ if len(trace1.sched_wakeup.data_frame) != 1:
+ raise RuntimeError('Test bug: bad sched_wakeup event count')
+
+ # Parse again without the window
+ trace1 = trappy.FTrace(
+ events=['sched_wakeup'],
+ window=(0, None))
+
+ self.assertEqual(len(trace1.sched_wakeup.data_frame), 2)
+
+ def test_cache_delete_single(self):
+ GenericFTrace.disable_cache = False
+ trace = trappy.FTrace()
+
+ trace_path = os.path.abspath(trace.trace_path)
+ trace_dir = os.path.dirname(trace_path)
+ trace_file = os.path.basename(trace_path)
+ cache_dir = '.' + trace_file + '.cache'
+ self.assertEquals(len(os.listdir(cache_dir)), 22)
+
+ os.remove(os.path.join(cache_dir, 'SchedWakeup.csv'))
+ self.assertEquals(len(os.listdir(cache_dir)), 21)
+
+ # Generate trace again, should regenerate only the missing item
+ trace = trappy.FTrace()
+ self.assertEquals(len(os.listdir(cache_dir)), 22)
+ for c in trace.trace_classes:
+ if isinstance(c, trace.class_definitions['sched_wakeup']):
+ self.assertEquals(c.cached, False)
+ continue
+ self.assertEquals(c.cached, True)